Life Management System Part 2

I've been out of commission for a little while now. When family comes over, suddenly I'm expected to do things... However of all days, Christmas, I'm able to get away for a little while to write this. Go figure. Anyway, this is a simple update to the life management system series. The first entry can be read here: Life Management System Part 1.

This post is a continuation of what I was showing in the first post. This time though I'm going to talk about some issues with the session code that cropped up and the solution that I'm using, minor change to the I/O mapping code, and also talk about the task manager code. If you'll remember in the first post I talked about the fact that the I/O mapping code had to be saved to the individual user's session since we may be talking about multiple people with potentially different I/O items (if this were set up in a client/server and specifically web app fashion). Well I started working on that issue and hit a slight wall. I was mapping each session to a User object. Well I never bothered to deal with the security issue so I had no way to differentiate individual users. I was simply using windows login on my web interface and never passing that information on to the system...

As simple a problem as it seems, it was actually more difficult than I thought since the core engine was suppose to be completely seperate from the interface. I mean the core should be able to be plugged into a web app, desktop app, client/server architecture, etc. It shouldn't matter. That means I have no guarenteed way to authenticate an individual. So after a couple of hours thinking about it, why not just push that off to the interface? So I came up with the following:

#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using BusinessObjects;
#endregion

namespace Core.Security.Interfaces
{
    /// <summary>
    /// Security manager interface
    /// </summary>
    public interface IManager
    {
        #region Properties

        /// <summary>
        /// Gets the current user
        /// </summary>
        User CurrentUser { get; }

        #endregion
    }
}

A simple interface that the interface implements and the core then loads. All I really need to know is who is the current user, I don't care about authenticating the user in the core. But it loads similarly to the other systems as shown in the first post. Load the assembly that I specify, load the classes, find our security manager, and create it. And in my test interface, I'm using windows authentication so all I need to do is this:

#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core.Security.Interfaces;
using BusinessObjects;
using System.Web;
#endregion

namespace WebSecurity
{
    /// <summary>
    /// Web security class
    /// </summary>
    public class WebSecurity:IManager
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public WebSecurity()
        {
        }

        #endregion

        #region IManager Members

        public BusinessObjects.User CurrentUser
        {
            get
            {
                User TempUser = User.Load(HttpContext.Current.User.Identity.Name);
                if (TempUser == null)
                {
                    TempUser = new User();
                    TempUser.FirstName = "New";
                    TempUser.LastName = "User";
                    TempUser.UserName = HttpContext.Current.User.Identity.Name;
                    TempUser.Password = "Password";
                    TempUser.Modifier = User.Load("Admin");
                    TempUser.Creator = TempUser.Modifier;
                    TempUser.Save();
                }
                return TempUser;
            }
        }

        #endregion
    }
}

You'll notice it's not that all it does is loads the user based on the user name, double checks if it exists, and if not creates a dummy account for the user. I'm not that worried about security since the site can only be accessed from my local machine while testing but in later versions I'll change that code a bit. But that's it, with that code I can finally figure out who the current user is and load their session information.

The benefit of that is the fact that it allowed me to fix the I/O mapping code such that it maps to the correct user:

namespace Core.IO
{
    /// <summary>
    /// IO Manager
    /// </summary>
    public class Manager
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public Manager()
        {

        }

        #endregion

        #region Public Properties

        /// <summary>
        /// Current IO Mappings
        /// </summary>
        public ListMapping<IInput, IOutput> IOMappings
        {
            get
            {
                ListMapping<IInput, IOutput> Mappings = null;
                ISession CurrentSession = Core.CoreManager.Instance.SessionManager.CurrentSession;
                if (CurrentSession["Core.IO.Manager.IOMappings"] == null)
                {
                    Mappings = new ListMapping<IInput, IOutput>();
                    CurrentSession["Core.IO.Manager.IOMappings"] = Mappings;
                }
                else
                {
                    Mappings = (ListMapping<IInput, IOutput>)CurrentSession["Core.IO.Manager.IOMappings"];
                }
                return Mappings;
            }
            set
            {
                ISession CurrentSession = Core.CoreManager.Instance.SessionManager.CurrentSession;
                CurrentSession["Core.IO.Manager.IOMappings"] = value;
            }
        }

        #endregion
    }
}

I simply moved the initial ListMapping (one of my data types in my utility library) into a manager which simply loaded/saved the ListMapping to the session. I do think that I need to come up with a way to lock down session objects so that they can only be accessed from specific areas, but other than that it works for now.

The last bit of code that I've been working on is the task portion of the core system. In my initial design I realized that there were certain things that needed to be done independently of the main system. For instance log files may need to be updated, temporary files may need to be deleted, etc. May need to be updated at different times/intervals. And these things may take a long time to finish. It may be possible that there are 5,000,000 temporary files that need to be deleted. Why should the main system wait for that? As such, instead of having the main thread wait, each task is set off in it's own worker thread to go about its business. This is fairly simple to do in .Net (well compared to the agony that I used to deal with in C++ circa 1999), so let's look at a bit of code real fast and discuss what I'm doing:

#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
#endregion

namespace Core.Tasks.Interfaces
{
    /// <summary>
    /// Task interface
    /// </summary>
    public interface ITask
    {
        #region Functions

        /// <summary>
        /// Called when the task is run
        /// </summary>
        void Process();

        #endregion

        #region Properties

        /// <summary>
        /// Number of milliseconds between calls
        /// </summary>
        int Timer { get; }

        #endregion
    }
}

First we have the interface that the individual task uses so we can find it and load it. But basically we just need two things from it. The first item is the interval, in milliseconds, between times it should be run. The second is a simple function called Process which runs the item once. It shouldn't loop indefinitely or wait to finish or anything. Just run once, do what it needs to do, and exit. The wrapper below handles all scheduling, etc.

#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Core.Tasks.Interfaces;
using System.Timers;
using System.Threading;
#endregion

namespace Core.Tasks
{
    /// <summary>
    /// Worker thread that holds the task
    /// </summary>
    public class Worker:Utilities.MultiThreading.Worker<int,string>
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="Params">Parameter as input (dummy input at this point)</param>
        /// <param name="Task">The task to hold</param>
        public Worker(string Params, ITask Task)
            : base(Params)
        {
            this.Task = Task;
        }

        #endregion

        #region Protected Overridden Functions

        protected override int Work(string Params)
        {
            while (!Stopping)
            {
                Thread.Sleep(Task.Timer);
                if (Task != null)
                {
                    Task.Process();
                }
            }
            return -1;
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// The task held by this thread
        /// </summary>
        public ITask Task { get; protected set; }

        #endregion
    }
}

The code above is simply a wrapper that handles the actual multithreading aspects of the task using a bit of code that I came up with a while ago. The only thing to note is that I added a protected property to indicate when the item should be stopped. Other than that, the code is similar. But anyway, the code above just uses one function really, Work. In that function we just wait the number of milliseconds indicated, and calls process on the task. That's it. And the manager itself is fairly simple as well:

#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Utilities.MultiThreading;
using System.Globalization;
using Core.Tasks.Interfaces;
#endregion

namespace Core.Tasks
{
    /// <summary>
    /// Manager for handling tasks
    /// </summary>
    public class Manager:IDisposable
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public Manager()
        {
            WorkerThreads = new List<Utilities.MultiThreading.Worker<int, string>>();
            Assembly TasksAssembly = Assembly.Load(CoreManager.Instance.SettingsInfo.TasksDLL);
            if (TasksAssembly != null)
            {
                Type[] Types = TasksAssembly.GetTypes();
                foreach (Type Type in Types)
                {
                    if (Type.GetInterface("ITask", true) != null)
                    {
                        WorkerThreads.Add(new Worker("default", (ITask)TasksAssembly.CreateInstance(Type.FullName)));
                    }
                }
            }
            for (int x = 0; x < WorkerThreads.Count; ++x)
            {
                WorkerThreads[x].Start();
            }
        }

        #endregion

        #region Private Properties

        private List<Worker<int, string>> WorkerThreads { get; set; }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            for (int x = 0; x < WorkerThreads.Count; ++x)
            {
                WorkerThreads[x].Stop();
            }
        }

        #endregion
    }
}

The code above just acts as a manager, loading the individual tasks and starting them. The only thing of note is the fact that I had to make it IDisposable so I could actually stop the worker threads. Other than that, it's like every other manager in the system thus far. Anyway, that's all there is to the task system. And actually that finishes out the basics of the core system (at least for now). The next couple of posts will be examples of each individual system as I use them in the web interface and as I get a bit further along, I'll put it up on codeplex so there is code to download. Anyway, take a look, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 12/25/2009 at 11:03 AM
Tags: , ,
Categories: ASP.Net | C#
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed

Using OAuth to Send Updates to Twitter Using C#

Well I'm not sure if it's better. To be honest it's a bit more difficult to set up initially but it's a bit more secure than the way I showed before. Anyway, this year (in fact early this year), Twitter started a transition to using OAuth. Since I'm new to messing with the service, I had no idea. But then again I'm pretty oblivious to most things, so I might have missed that information even if I had been an early adopter. That being said, I've talked about OAuth before and I'll assume that you've read the post here. If not take a look before looking at the code below. Anyway, enough stalling, let's jump into some code:

/*
Copyright (c) 2009 <a href="http://www.gutgames.com">James Craig</a>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/


#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Utilities.Web.OAuth;
using System.Text.RegularExpressions;
using System.Web;
#endregion

namespace Utilities.Web.Twitter
{
    /// <summary>
    /// Helper class to deal with Twitter
    /// </summary>
    public class Twitter:OAuth.OAuth
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public Twitter()
            : base()
        {
            this.SignatureType = Signature.HMACSHA1;
        }

        #endregion

        #region Public Functions

        /// <summary>
        /// Gets a request token/token secret
        /// </summary>
        /// <param name="Token">The request token</param>
        /// <param name="TokenSecret">The request token secret</param>
        public void GetRequestToken(out string Token,out string TokenSecret)
        {
            this.Token = "";
            this.TokenSecret = "";
            this.Method = HTTPMethod.GET;
            this.Url = new Uri("http://twitter.com/oauth/request_token");
            REST.REST RestHelper = new Utilities.Web.REST.REST();
            RestHelper.Url = new Uri(GenerateRequest());
            string Value = RestHelper.GET();
            Regex TokenRegex = new Regex("oauth_token=(?<Value>[^&]*)");
            Match TempToken = TokenRegex.Match(Value);
            Token = TempToken.Groups["Value"].Value;
            Regex TokenSecretRegex = new Regex("oauth_token_secret=(?<Value>[^&]*)");
            Match TempTokenSecret = TokenSecretRegex.Match(Value);
            TokenSecret = TempTokenSecret.Groups["Value"].Value;
        }

        /// <summary>
        /// Gets the location of the authorization site (requires
        /// request token/token secret)
        /// </summary>
        /// <returns>The location that the user must go in order to
        /// authorize the app and get the authorization PIN</returns>
        public string GetAuthorizationSite()
        {
            this.Method = HTTPMethod.GET;
            this.Url = new Uri("http://twitter.com/oauth/authorize");
            this.AddParameter("oauth_callback", "oob");
            return new Uri(GenerateRequest()).ToString();
        }

        /// <summary>
        /// Gets the access token/token secret which are used in actual calls
        /// (requires the PIN from the authorization site and the request token
        /// and request token secret)
        /// </summary>
        /// <param name="PIN">PIN received from the authorization site</param>
        /// <param name="AccessToken">The access token</param>
        /// <param name="AccessTokenSecret">The access token secret</param>
        public void GetAccessToken(string PIN, out string AccessToken, out string AccessTokenSecret)
        {
            this.Url = new Uri("http://twitter.com/oauth/access_token");
            this.AddParameter("oauth_verifier", PIN);
            this.Method = HTTPMethod.POST;
            REST.REST RestHelper = new Utilities.Web.REST.REST();
            RestHelper.Url = new Uri(GenerateRequest());
            string Value = RestHelper.POST();
            Regex TokenRegex = new Regex("oauth_token=(?<Value>[^&]*)");
            Match TempToken = TokenRegex.Match(Value);
            AccessToken = TempToken.Groups["Value"].Value;
            Regex TokenSecretRegex = new Regex("oauth_token_secret=(?<Value>[^&]*)");
            Match TempTokenSecret = TokenSecretRegex.Match(Value);
            AccessTokenSecret = TempTokenSecret.Groups["Value"].Value;
        }

        /// <summary>
        /// Updates the status of the user
        /// </summary>
        /// <param name="Status">Status of the user (needs to be within 140 characters)</param>
        /// <returns>The XML doc returned from the Twitter service</returns>
        public string UpdateStatus(string Status)
        {
            this.Method = HTTPMethod.POST;
            this.Url = new Uri("http://twitter.com/statuses/update.xml");
            this.AddParameter("status", Status);
            REST.REST RestHelper = new Utilities.Web.REST.REST();
            RestHelper.Url = new Uri(GenerateRequest());
            return RestHelper.POST();
        }

        /// <summary>
        /// Gets a user's timeline from the Twitter service
        /// </summary>
        /// <param name="UserName">The screen name of the user</param>
        /// <returns>The XML doc returned from the Twitter service
        /// contatining the timeline</returns>
        public string GetTimeline(string UserName)
        {
            this.Method = HTTPMethod.POST;
            this.Url = new Uri("http://twitter.com/statuses/user_timeline.xml");
            this.AddParameter("screen_name", UserName);
            REST.REST RestHelper = new Utilities.Web.REST.REST();
            RestHelper.Url = new Uri(GenerateRequest());
            return RestHelper.GET();
        }

        #endregion
    }
}

The code above uses the OAuth helper class (modified, from the post that I link to so go to the svn store on codeplex to see the latest version) along with a new REST helper class that I created to work with Twitter. But before I show the REST class, I should explain the code above. There are a number of functions here that you need to call prior to ever being able to send anything to Twitter. The first is GetRequestToken. This does what it sounds like. It gets a request token from the Twitter service. The request token is then used to register an app with a user. Prior to authorizing the app, you can't do a damn thing when using OAuth. The request token/token secret that is returned by the Twitter service are then used in the GetAuthorizationSite function. This function simply gets you the location that the user must go in order to authorize your app to work with their account. The user goes to that location, clicks on Allow and is then given a PIN number. Note that during all of this you have to hold onto the request token/secret token so that you can use them along with the PIN in the next function: GetAccessToken. This function actually trades in the request token that you're using for an access token/token secret. These are then used in all of the functions that actually do anything...

That's right, you just went through three steps just so you can start to use the service. It's annoying but on the bright side, it's much more secure. Not to mention it tends to make those who are paranoid trust you a bit more as they are no longer giving you their user name and password. Anyway, the other two functions in the class do two things. The first is a status update function. It's basically what I showed you before in the previous post but cleaned up and using OAuth instead of basic authentication. The second function allows you to query Twitter for a timeline of a single user. It returns an XML file that you'll need to parse, but it does get the job done for now. They should be fairly simple to follow... Well, they would be if I had shown you the REST class before now. So you're not completely confused here it is:

/*
Copyright (c) 2009 <a href="http://www.gutgames.com">James Craig</a>

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.*/


#region Usings
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.IO;
#endregion

namespace Utilities.Web.REST
{
    /// <summary>
    /// Class designed to help with calling REST based applications
    /// </summary>
    public class REST
    {
        #region Constructor

        /// <summary>
        /// Constructor
        /// </summary>
        public REST()
        {
        }

        #endregion

        #region Public Functions

        /// <summary>
        /// Does a GET to the REST service
        /// </summary>
        /// <returns>a string containing the data returned by the service</returns>
        public string GET()
        {
            try
            {
                HttpWebRequest Request = WebRequest.Create(Url) as HttpWebRequest;
                Request.Method = "GET";
                Request.ContentType = "text/xml";
                SetupData(Request);
                SetupCredentials(Request);
                return SendRequest(Request);
            }
            catch { throw; }
        }

        /// <summary>
        /// Does a POST to the REST service
        /// </summary>
        /// <returns>a string containing the data returned by the service</returns>
        public string POST()
        {
            try
            {
                HttpWebRequest Request = WebRequest.Create(Url) as HttpWebRequest;
                Request.Method = "POST";
                Request.ContentType = "application/x-www-form-urlencoded";
                SetupData(Request);
                SetupCredentials(Request);
                return SendRequest(Request);
            }
            catch { throw; }
        }

        /// <summary>
        /// Does a DELETE on the REST service
        /// </summary>
        /// <returns>a string containing the data returned by the service</returns>
        public string DELETE()
        {
            try
            {
                HttpWebRequest Request = WebRequest.Create(Url) as HttpWebRequest;
                Request.Method = "DELETE";
                Request.ContentType = "application/x-www-form-urlencoded";
                SetupData(Request);
                SetupCredentials(Request);
                return SendRequest(Request);
            }
            catch { throw; }
        }

        /// <summary>
        /// Does a PUT on the REST service
        /// </summary>
        /// <returns>a string containing the data returned by the service</returns>
        public string PUT()
        {
            try
            {
                HttpWebRequest Request = WebRequest.Create(Url) as HttpWebRequest;
                Request.Method = "PUT";
                Request.ContentType = "application/x-www-form-urlencoded";
                SetupData(Request);
                SetupCredentials(Request);
                return SendRequest(Request);
            }
            catch { throw; }
        }

        #endregion

        #region Private Functions

        /// <summary>
        /// Sets up any data that needs to be sent
        /// </summary>
        /// <param name="Request">The web request object</param>
        private void SetupData(HttpWebRequest Request)
        {
            if (string.IsNullOrEmpty(Data))
            {
                Request.ContentLength = 0;
                return;
            }
            try
            {
                byte[] ByteData = UTF8Encoding.UTF8.GetBytes(Data);
                Request.ContentLength = ByteData.Length;
                using (Stream RequestStream = Request.GetRequestStream())
                {
                    RequestStream.Write(ByteData, 0, ByteData.Length);
                }
            }
            catch { throw; }
        }

        /// <summary>
        /// Sets up any credentials (basic authentication,
        /// for OAuth, please use the OAuth class to create the
        /// URL)
        /// </summary>
        /// <param name="Request">The web request object</param>
        private void SetupCredentials(HttpWebRequest Request)
        {
            try
            {
                if (!string.IsNullOrEmpty(UserName) && !string.IsNullOrEmpty(Password))
                {
                    Request.Credentials = new NetworkCredential(UserName, Password);
                }
            }
            catch { throw; }
        }

        /// <summary>
        /// Sends the request to the URL specified
        /// </summary>
        /// <param name="Request">The web request object</param>
        /// <returns>The string returned by the service</returns>
        private string SendRequest(HttpWebRequest Request)
        {
            try
            {
                using (HttpWebResponse Response = Request.GetResponse() as HttpWebResponse)
                {
                    if (Response.StatusCode != HttpStatusCode.OK)
                        throw new Exception("The request did not complete successfully and returned status code " + Response.StatusCode);
                    using (StreamReader Reader = new StreamReader(Response.GetResponseStream()))
                    {
                        return Reader.ReadToEnd();
                    }
                }
            }
            catch { throw; }
        }

        #endregion

        #region Public Properties

        /// <summary>
        /// URL to send the request to
        /// </summary>
        public Uri Url { get; set; }

        /// <summary>
        /// Any data that needs to be appended to the request
        /// </summary>
        public string Data { get; set; }

        /// <summary>
        /// User name (basic authentication)
        /// </summary>
        public string UserName { get; set; }

        /// <summary>
        /// Password (basic authentication)
        /// </summary>
        public string Password { get; set; }

        #endregion
    }
}

If you've ever dealt with a REST API before, it's pretty basic. But more or less it's divided into four functions, GET, PUT, POST, DELETE. It also allows for basic authentication if you need it. But basically you just load up the URL, any data that needs to be appended, user name and password and call the appropriate function. It in turn returns anything that the server sends back. So there you go, basic REST helper and Twitter helper classes. It's not that difficult to build off of them, so take a look, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 12/9/2009 at 10:11 AM
Tags: , ,
Categories: C#
Post Information: Permalink | Comments (0) | Post RSSRSS comment feed

Life Management System Part 1

Finally done with Thanksgiving. I've always said that you can only take so much family before someone has to be thrown out the window. Oh, and I know this should really be part 2 but I hate it when people have a part 1 that is all text and no code. It doesn't help you get a grasp of what they're doing. So even though I talked about the project before, I consider this part 1. However if you want to read my ramblings on the project, you can do so here.

As far as setup of the system, I'm using my utility library, HaterAide, and YABOV.  The use of HaterAide means that I'll have to change it slightly. It was originally designed for prototyping of a website but I'll have to end up modifying it to work with different types of applications. Past that the general layout of the system thus far is slightly different from what I thought it would be but it's currently this:

 

The system is divided into the following systems: IO, Processor, Services, Session, Settings, Tasks, and a core that controls everything. It's a little difficult to tell from the diagram which is which considering it doesn't show namespaces (also note that Tasks and Settings are not shown here). Anyway, the core is a singleton and holds the various systems, creates them, etc. The basic layout is as follows:

 

namespace Core
{
    public class CoreManager : Singleton<CoreManager>
    {
        protected CoreManager()
            : base()
        {

        }

        public void Setup()
        {
            LoadSettings();

            IOMappings = new ListMapping<IInput, IOutput>();
            ProcessorManager = new Processor.Manager();
            ServiceManager = new Services.Manager();
            SessionManager = new Session.Manager();
            TaskManager = new Tasks.Manager();
        }

        private void LoadSettings()
        {
            BusinessObjects.GlobalFunctions.Setup("CONNECTIONSTRINGFORSQL");
            System.Collections.Generic.List<Settings> SettingsList = BusinessObjects.Settings.LoadAll();
            for (int x = 0; x < SettingsList.Count; ++x)
            {
                if (SettingsList[x].Active)
                {
                    SettingsInfo = SettingsList[x];
                    return;
                }
            }

            SettingsInfo = new Settings();
            SettingsInfo.ProcessorDefaultClass = "Processor";
            SettingsInfo.SessionDefaultClass = "Session";
            SettingsInfo.ServicesDLL = "Services";
            SettingsInfo.Save();
        }

        public void Process()
        {
            foreach (IInput Input in IOMappings.Keys)
            {
                IValue Value = Input.GetInput();
                if (Value != null)
                {
                    Value = ProcessorManager.PreProcess(Input.GetInput());
                    System.Collections.Generic.List<IValue> OutputValues = ServiceManager.Process(Value);
                    ProcessorManager.PostProcess(OutputValues);
                    foreach (IOutput Output in IOMappings[Input])
                    {
                        Output.SetOutput(OutputValues);
                    }
                }
            }
        }

        public ListMapping<IInput, IOutput> IOMappings { get; protected set; }
        public Processor.Manager ProcessorManager { get; protected set; }
        public Services.Manager ServiceManager { get; protected set; }
        public Session.Manager SessionManager { get; protected set; }
        public Tasks.Manager TaskManager { get; protected set; }

        public Settings SettingsInfo { get; protected set; }
    }
}

 

The code above wont make much sense without the other systems but for now you can look at it in generalities. Initially the core is created, but note that the objects are not created at the same time. The reason for this is the way in which the Singleton class from my utility library works. The Singleton class creates the object and saves it in the base constructor. The base constructor is called after the class's constructor and since the ProcessorManager, ServiceManager, etc. will be using the core, we have to create the objects outside of the constructor. This leads us to the Setup function, which is pretty basic. However it calls the LoadSettings function, which in turn loads a class which is not even in the Core DLL. Instead it's using one of the classes from the BusinessObjects DLL. The BusinessObjects assembly currently looks like this:

 

Basically there are only two types here at present: Settings and User. I could break down settings into a list of type setting, which I may do, but at this time there isn't much of a point. Anyway, they both inherit from BaseObject. Thanks to how I created HaterAide, I can have the BaseObject type handle saving, deleting, loading, etc. On top of this, it contains a number of events that I can use to hook into the object for logging, etc. As such the individual objects don't need to do much other than set up the properties (which need to be virtual due to HaterAide) and any specific functions that it may need.

So back to the LoadSettings function. As I said, it loads the Settings objects and finds which one is active (current). It also calls a global function in the BusinessObjects DLL that simply starts up HaterAide:

        public static void Setup(string ConnectionString)
        {
            new Factory(typeof(IObject).Assembly, ConnectionString);
        }

 

IObject is simply the interface that the BaseObject class uses (which in turn has the ID, DateCreated, etc. properties). But anyway that's all that the LoadSettings function does. The rest of the Setup function simply creates the various subsystems (IO, Services, etc.). The IO class is simpy a ListMapping (another class from the utility library). Its only purpose is to hold each input item and map it to interested output items. The interface has to set these up. Although it shouldn't be in the core class as it turns out. Well not exactly anyway. Since, for now, I'm using a web interface it actually needs to be mapped at the session level. The core is static and thus constant even in a web app (more or less anyway). However I may have multiple people using this at once and so I need this to be mapped to an individual user's session. And since I'm using a web app there are actually other potential issues as well.

One of the main issues with a web app is the way that a page is created. When a request comes in, it creates a brand new Page object. It does this every time. The information is stored in the view state, which people think means that the object is still there in between requests. It's not. Request comes in, it creates a brand new Page object, loads info based on view state, does its thing, sends back a response, and destroys the Page object. Every time. That means that all of those buttons are new buttons, every time. So I can't even store the IO mappings across sessions. In reality the interface needs to set it up each and every time. So I still need to work on this aspect and figure out some way to automatically do this without writing tons of code in the interface portion.

The Processor, Session, and Services code is a bit simpler and more uniform actually. For example the Services manager code looks like this:

 

namespace Core.Services
{
    public class Manager
    {
        public Manager()
        {
            Services = new List<IService>();
            Assembly ServiceAssembly = Assembly.Load(CoreManager.Instance.SettingsInfo.ServicesDLL);
            if (ServiceAssembly != null)
            {
                Type[] Types = ServiceAssembly.GetTypes();
                foreach (Type Type in Types)
                {
                    if (Type.GetInterface("IService", true) != null)
                    {
                        Services.Add((IService)ServiceAssembly.CreateInstance(Type.FullName));
                    }
                }
            }
        }

        public List<IValue> Process(IValue Value)
        {
            List<IValue> Values = new List<IValue>();
            foreach (IService Service in Services)
            {
                IValue OutputValue = Service.Process(Value);
                if (OutputValue != null)
                {
                    Values.Add(OutputValue);
                }
            }
            return Values;
        }

        protected List<IService> Services { get; set; }
    }
}

 

The code above loads the services that it finds in whatever assembly is specified in the settings. You may want to note that you can't really load an assembly based on location. For example you can't have an assembly in directory Services and expect it to find it (unless you set up the app.config or web.config files correctly). It needs to either be in the GAC or the bin for the app (unless, like I said, you set up the app.config to look in a different directory). Past that, it looks for all IService objects which is simply an interface for the services. It then loads each one. The code for the processor manager and session manager look fairly similar. The task manager hasn't been implemented yet though, but will look very similar as well.

If we go back to the original CoreManager class, you'll see that there is one more function: Process. The process function is called every time there is a request. Every time the interface (or input, or whatever) wants to make a request, it calls that function. It in turn gets input from all input sources. Looping through each one, it sends that input to be preprocessed (which is useful if we're dealing with things like images, sound, etc. to get it in the format we need). After that, we send it off to the services. These in turn come back with a list of outputs that we may be interested in. For example if I send in Kung Fu Hustle, one service may give me back a link to play the film, one may give me information on said film, one may give a list of actors, etc. These items are then sent to the processor for post processing (make sure the info is "safe", etc.).  Once that is done, we simply send it off to each output object that wants to be notified. The output object then does what it wants with the information.

That's all there is to it at present. I've also created a single service, processor, and session class so that it runs. Well that and an input object (TextBox) and output object (Label). But at this point the service just acts as an echo, the processor doesn't do anything, and the session class is simply using the web app's built in session. So there isn't much in the way of interesting code to be found there. In the coming weeks I'll build on it though and come up with... something. Anyway, I hope someone out there found this interesting. Oh and before I forget HaterAide, YABOV, and Craig's Utility Library are now using the SVN server on CodePlex. So as I do bug fixes, changes, etc. you can get them a bit faster. Note that I tend to tear things down and rebuild so use it at your own risk. Anyway, take a look, leave feedback, and happy coding.

kick it on DotNetKicks.com   Shout it
Digg It!DZone It!StumbleUponTechnoratiRedditDel.icio.usNewsVineFurlBlinkListEmail

Posted by: James Craig
Posted on: 12/1/2009 at 11:03 AM
Tags: , , , ,
Categories: C#
Post Information: Permalink | Comments (1) | Post RSSRSS comment feed