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

Comments

Gut Instinct

Tuesday, January 05, 2010 11:56 AM

trackback

Life Management System Part 3

Life Management System Part 3

Add comment




  Country flag

biuquote
  • Comment
  • Preview
Loading