Jim Scott's Coding Blog

It's all about that code, that code, that code, no treble

My favorite design patterns are Strategy and Factory Method

A number of years back I started my exploration into the world of design patterns and read an incredible book “Design Patterns Explained” that did a great job in getting me started on a path that would ultimately change the way I developed code forever.

As I learned more about design patterns I began to see that in many cases I was already using some of those patterns. When I finished with the book I was armed with knowing about design patterns but was not exactly sure how some of those patterns would be put into practice. I knew the only way I was going to actually use these patterns was to writing code and attempting to use the patterns I had learned.

Jump forward a few years now and the two patterns that I see used the most in my software projects are the Strategy Pattern part of the Behavioral Pattern family and Factory Method Pattern part of the Creational Pattern family.  When I typically use these two patterns they are almost always used together, the strategy pattern for implementing different behaviors that my application will use and the factory method pattern to encapsulate the logic required for deciding which concrete implementation of the strategy will be used.

In order to demonstrate the use of these patterns I am going to create a simple application that I may have typically written in the past that could benefit from these two patterns. Unfortunately when I was learning about many of the patterns what was never really clear was when a pattern might typically be used to improve the code I was currently writing. I hope that by the end of the article you will see for at least these two patterns how you might be able to implement these patterns.

Application Requirements

You will be building a simple console application that will be used to check to see if a website is up. The console application will initially be run manually by an individual responsible for making sure that the various websites are up and responding. The application must be able to check the content of the returned HTML for a given string to ensure that the content retrieved is returning the expected information. The application must take the following pieces of information as input (website address, content to look for) and then return the word SUCCESS if it found the content and ERROR followed by another line indicating the reason the test failed. The application should also write out the steps that it is performing to provide feedback to the person running the application.

Application V1.0 – First version written to deal with the current requirements. (Note: do not get caught up in the lack of validation of input parameters as the sample is designed to focus on the strategy pattern not best practices for writing the application as a whole.)

using System;
using System.Net;

namespace Design_Patterns
{
    internal class Program
    {
        f(string[] args)
        {
            // More validation would be necessary but the article is not
            // about validation of input ;-)
            if (args.Length != 2)
            {
                Console.WriteLine("You must supply the URL and Content to check for.");
            }

            string websiteUrl = args[0];

            Console.WriteLine("URL supplied to be validated: {0}", websiteUrl);

            string contentToVerify = args[1];

            Console.WriteLine("Content to be verified: {0}", contentToVerify);

            WebClient webClient = new WebClient();

            try
            {
                Console.WriteLine("Attempting to retrieve content");

                string htmlContent = webClient.DownloadString(websiteUrl);

                Console.WriteLine("Content downloaded, checking to see if content exists.");

                if (htmlContent.Contains(contentToVerify))
                {
                    Console.WriteLine("SUCCESS: Content supplied was found.");
                }
                else
                {
                    Console.WriteLine("ERROR: Content was not found in the returned html.");
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine("ERROR: Exception occurred while downloading content: {0}", ex.Message);
            }

            Console.WriteLine("-- Hit enter To close application –");

            Console.Read();
        }
    }
}

 

Application Results – Running the application from a command prompt to test and see pulling down the html for www.google.com we could find the content “Google Search” and things worked as expected..

image

 

 

Application Revision

The application designed has meet the needs expected, however after using it for a period of time it is realized that while it is great that someone can open the application and manually verify that a website is up and returning the expected content, they would like to make the process automated instead of manually running have it run on a regular basis and then have the person responsible check periodically to see the results of the tests. It is proposed that you log the output of the results to a text file that the person will check.

Application V1.1 – Second version to add ability to run manually or automatically and log results to a log file when automated.

using System;
using System.IO;
using System.Net;

namespace Design_Patterns
{
    internal class Program
    {
        private static StreamWriter fileWriter = null;
        private static bool useFileWriter = false;
        private const string loggingFileName = "WebsiteMonitor.log";

        private static void Main(string[] args)
        {
            try
            {
                // More validation would be necessary but the article is not
                // about validation of input ;-)
                if (args.Length < 2)
                {
                    WriteMessage("You must supply the URL and Content to check for."); return;
                }
                // If third argument supplied then it is used to determine
                // if the application will log to a file
                if (args.Length == 3)
                {
                    useFileWriter = Convert.ToBoolean(args[2]);

                    if (useFileWriter)
                    {
                        fileWriter = File.CreateText(loggingFileName);
                    }
                }

                string websiteUrl = args[0];

                WriteMessage(string.Format("URL supplied to be validated: {0}", websiteUrl));

                string contentToVerify = args[1];

                WriteMessage(string.Format("Content to be verified: {0}", contentToVerify));

                WebClient webClient = new WebClient();

                try
                {
                    WriteMessage("Attempting to retrieve content");

                    string htmlContent = webClient.DownloadString(websiteUrl);

                    WriteMessage("Content downloaded, checking to see if content exists.");

                    if (htmlContent.Contains(contentToVerify))
                    {
                        WriteMessage("SUCCESS: Content supplied was found.");
                    }
                    else
                    {
                        WriteMessage("ERROR: Content was not found in the returned html.");
                    }
                }
                catch (Exception ex)
                {
                    WriteMessage(string.Format("ERROR: Exception occurred while downloading content: {0}", ex.Message));
                }

                WriteMessage(string.Format("Test completed at: {0}", DateTime.Now));

                if (!useFileWriter)
                {
                    WriteMessage("-- Hit enter To close application --");

                    Console.Read();
                }
            }
            finally
            {
                if (fileWriter != null) fileWriter.Dispose();
            }
        }

        /// <summary>
        /// Writes the message to either the console or file based on configuration
        /// </summary>
        /// <param name="message"></param>
        private static void WriteMessage(string message)
        {
            if (!useFileWriter)
            {
                Console.WriteLine(message);
            }
            else
            {
                fileWriter.WriteLine(message);
            }
        }
    }
}

 

Application Results - Now when you run the application manually as before you get the same output to the console. However you can also run and provide a third argument of true and the output will be written to a log file named WebsiteMonitor.log with the results. The issue with the above code is that the main method now has to be aware of how to write the message in the two different ways. You can imagine that if asked to add a third way of logging to an XML file, the main method starts to get even more complex and its responsibility of knowing about logging becomes a bit out of hand.

image

 

 

Solution

Implement the strategy pattern to deal with the various ways the application would like to implement various logging algorithms.

Strategy Pattern

When you look at any article that discusses the strategy pattern you will typically find a UML diagram that shows the various items that make up the pattern. The following UML diagram is an example of the strategy pattern that I will be implementing for logging messages in example code.

image

 

 

Let me take a minute to explain what the above UML diagram says about the logging I will be implementing.

  • Logger – Represents the context class that will contain the logging strategy. This is the class that our client code (Program.cs main method) will use to implement logging of messages for the system. The line between the Logger and the ILogWriter with the filled diamond indicates that our Logger class will always have a ILogWriter instance. Notice also that the Logger class has a constructor that takes an ILogWriter object that you will see comes into play as we refactor the existing code.
  • ILogWriter – Represents the logging strategy that we are implementing for the application. In this case the ILogWriter is an interface that defines that all strategy classes must implement a Write method that will be used to write the message to be logged. However the interface does not contain any implementation but rather defines the contract for any concrete class that implement the interface.
  • ConsoleWriter, XmlWriter, FileWriter – Represents the concrete classes that will represent each of the logging algorithms that can be used by the application. The dashed lines between the concrete classes and strategy indicate that the concrete classes implement the ILogWriter interface.

Application V1.2 – Use the strategy pattern to remove some of the complexity in the main method and remove it from being responsible for having to understand the various algorithms required for doing logging of messages.

Create strategy – ILogWriter interface that specific logging algorithms will use.

namespace Design_Patterns
{
    public interface ILogWriter 
    {
        void Write(string message);
    }
}

Create the new concrete classes that will do the logging for Console and File logging

Console Writer – Class that will be responsible for logging messages to the console

using System;

namespace Design_Patterns
{
    public class ConsoleWriter : ILogWriter 
    {
        public void Write(string message)
        {
            Console.WriteLine(message);
        }
    }
}

 

File Writer – Class that will be responsible for logging messages to a file

using System;
using System.IO;

namespace Design_Patterns
{
    public class FileWriter : ILogWriter, IDisposable
    {
        private const string logFileName = "WebsiteMonitor.log";
        private StreamWriter fileWriter = null;

        public FileWriter()
        {
            fileWriter = File.CreateText(logFileName);
            fileWriter.AutoFlush = true;
        }

        public void Write(string message)
        {
            fileWriter.WriteLine(message);
        }

        /// <summary>
        /// Dispose method ensures that the file is closed and disposed
        /// </summary>
        public void Dispose()
        {
            if (fileWriter != null) fileWriter.Dispose();
        }

    }
}

Logger – Client in strategy pattern that will contain an instance of the strategy to do the logging for the application. This class will be used inside the Program main method to do the logging for the application. The default  logger that will be used if not defined is the console logger, this allows us to implement the same behavior that the original application had without any extra configuration. You can also alternatively pass in the type of logger you wish to create or pass in a specific strategy required, this will be important when we implement the final refactoring as you will see in a bit.

using System;

namespace Design_Patterns
{
    public class Logger : IDisposable
    {
        private ILogWriter logger;

        /// <summary>
        /// Default constructor will implement a console writer
        /// </summary>
        public Logger()
        {
            logger = new ConsoleWriter();
        }

        /// <summary>
        /// Initializes a new logger based on the LoggerType provided
        /// </summary>
        /// <param name="loggerType">LoggerType to use</param>
        public Logger(LoggerType loggerType)
        {
            switch (loggerType)
            {
                case LoggerType.Console:
                    logger = new ConsoleWriter();
                    break;
                case LoggerType.File:
                    logger = new FileWriter();
                    break;
                case LoggerType.Xml:
                    logger = new XmlWriter();
                    break;
                default:
                    throw new Exception("LoggerType unknown: " + loggerType);
            }
        }

        /// <summary>
        /// Logger type can be passed in
        /// </summary>
        /// <param name="logger">ILogWriter</param>
        public Logger(ILogWriter logger)
        {
            this.logger = logger;
        }

        /// <summary>
        /// Write message to logger
        /// </summary>
        /// <param name="message"></param>
        public void Write(string message)
        {
            logger.Write(message);
        }

        /// <summary>
        /// Dispose logger if IDisposable is implemented
        /// </summary>
        public void Dispose()
        {
            if (logger is IDisposable && logger != null)
            {
                ((IDisposable)logger).Dispose();
            }
        }
    }
}

 

Program – Application code that has now been refactored to use the strategy pattern. Take note that this class no longer knows anything about how to perform logging. The only thing that remains regarding logging is the code that decides what type of logger the application should use. These changes will provide an approach that will allow our application to expand and use different loggers as requirements change without having to change the logic in the main method. The program also no longer needs to know anything about how logging happens. The WriteMessage method has now been removed, the application when started looks to see if file logging is required and if so instantiates a new logger passing in that it will need to implement file logging, otherwise the default logger is initialized which will do console logging which is the same as the previous behavior.

using System;
using System.Net;

namespace Design_Patterns
{
    internal class Program
    {
        private static bool useFileWriter = false;

        private static void Main(string[] args)
        {
            Logger logger = null;

            try
            {
                // If third argument supplied then it is used to determine
                // if the application will log to a file
                if (args.Length == 3)
                {
                    useFileWriter = Convert.ToBoolean(args[2]);

                    if (useFileWriter)
                    {
                        logger = new Logger(LoggerType.File);
                    }
                }

                if (logger == null)
                {
                    logger = new Logger();
                }

                // More validation would be necessary but the article is not
                // about validation of input ;-)
                if (args.Length < 2)
                {
                    logger.Write("You must supply the URL and Content to check for.");

                    return;
                }

                string websiteUrl = args[0];

                logger.Write(string.Format("URL supplied to be validated: {0}", websiteUrl));

                string contentToVerify = args[1];

                logger.Write(string.Format("Content to be verified: {0}", contentToVerify));

                WebClient webClient = new WebClient();

                try
                {
                    logger.Write("Attempting to retrieve content");

                    string htmlContent = webClient.DownloadString(websiteUrl);

                    logger.Write("Content downloaded, checking to see if content exists.");

                    if (htmlContent.Contains(contentToVerify))
                    {
                        logger.Write("SUCCESS: Content supplied was found.");
                    }
                    else
                    {
                        logger.Write("ERROR: Content was not found in the returned html.");
                    }
                }
                catch (Exception ex)
                {
                    logger.Write(string.Format("ERROR: Exception occurred while downloading content: {0}", ex.Message));
                }

                logger.Write(string.Format("Test completed at: {0}", DateTime.Now));

                if (!useFileWriter)
                {
                    logger.Write("-- Hit enter To close application --");

                    Console.Read();
                }
            }
            finally
            {
                if (logger != null) logger.Dispose();
            }
        }
    }
}

 

Our application however still needs to know a bit about how to create an instance of the Logger class and the various strategy classes, to solve this issue we are going to implement the Factory Method pattern which will be responsible for creating new instances. This will help when we decide to have some of our concrete strategy classes get more complex which could make our Logger class complex in understanding how to create the various instances in our switch statement. If you see the new statement used someplace it is probably a good indicator that a factory method pattern could be used. This article is getting a bit longer than I expected so I am going to do the next pattern in a part 2 article.

Stay Tuned!

Free collection of EBooks for various Microsoft Technologies

Found a page on the Technet site that provides a pretty good list of eBooks on various Microsoft Technologies

http://social.technet.microsoft.com/wiki/contents/articles/11608.e-book-gallery-for-microsoft-technologies.aspx

Topics covered

Office, SharePoint, SQL Server, System Center, Visual Studio, Web Development, Windows, Windows Azure, Windows Phone and Windows Server

Here is a twitter page also that free Microsoft eBooks get posted to as well

https://twitter.com/MSFreeEBooks

Windows 8 Hyper-V errors with "could not be started because the hypervisor is not running"

Just recently setup a new machine with Windows 8 and decided to install Hyper-V so that I could run windows XP for some of my legacy applications that will not run under Windows 8 64 bit. After installing Hyper-V components I attempted to setup a new virtual machine and start the install process when I was faced with the following error: "could not be started because the hypervisor is not running"

It turned out that I needed to run the following command

bcdedit /set hypervisorlaunchtype auto

After running the above command and restarting my machine it now works. Hope this helps save someone else hours they cannot back due to this issue.

Calling WCF Service using LINQPad

First, if you are a .NET developer and you are not using LINQPad yet go and check it out at www.linqpad.net. They offer a free version that is the same as the paid version but without intellisense and the pricing to upgrade to the full version is well worth the productivity gain you get.

Ok, with that out of the way let me get to the issue I came across and how I solved it.

Issue:

Created an assembly project in order to create a proxy to a web services using .NET Service Reference which I would then use in LINQPad to do some testing against my web service. Created a new linqpad script and added my reference to the assembly and wrote a method to call my web service. Upon executing I received an InvalidOperationException:

Could not find default endpoint element that references contract 'WCFAcctMgmtWebServiceReference.AccountManagementWebservicesSoap' in the ServiceModel client configuration section. This might be because no configuration file was found for your application, or because no endpoint element matching this contract could be found in the client element.

That is odd I can see that my assembly includes a file Proxy.exe.config that has the necessary service reference information

<system.serviceModel>
	<bindings>
		<basicHttpBinding>
			<binding name="AccountManagementWebservicesSoap" />
		</basicHttpBinding>
	</bindings>
	<client>
		<endpoint address="http://ws.domain.com/acctmgmt/v4/service.asmx"
			binding="basicHttpBinding" bindingConfiguration="AccountManagementWebservicesSoap"
			contract="WCFAcctMgmtWebServiceReference.AccountManagementWebservicesSoap"
			name="AccountManagementWebservicesSoap" />
	</client>
</system.serviceModel>

So why was it not working?

Answer:

Well it turns out that LINQPad needs to have this configuration information included in a file it uses since it is the application executing the proxy. At first glance one might assume they would place it in the the file included with LINQPad called LINQPad.exe.config but that would result in the same issue.

Instead you need to create a file in the same directory that LINQPad is installed in and name it linqpad.config

Adding the following to my new file resolved the issue

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
	<system.serviceModel>
		<bindings>
			<basicHttpBinding>
				<binding name="AccountManagementWebservicesSoap" />
			</basicHttpBinding>
		</bindings>
		<client>
			<endpoint address="http://ws.domain.com/acctmgmt/v4/service.asmx"
				binding="basicHttpBinding" bindingConfiguration="AccountManagementWebservicesSoap"
				contract="WCFAcctMgmtWebServiceReference.AccountManagementWebservicesSoap"
				name="AccountManagementWebservicesSoap" />
		</client>
	</system.serviceModel>
</configuration>

 Hopefully this article will save someone the time I spent figuring this out.

Running FSCK on an LVM (Logical Volume Manager) using Linux Rescue Disk

I am not a linux expert and so when I have problems from time to time I usually have to go searching for the answer. It usually takes me looking at several articles to get my answer and when I am finished I always try and put together a article that has everything I need to know in once spot. Please feel free to correct or suggest a better alternative if you come across this article and would like to provide feedback.

Yesterday I had a server crash and when I logged into the console it was showing a kernal panic!!

I rebooted and the same thing happened over on every reboot. This machine had some problems in the past with the disk going into read only mode so I assumed the issue was related to something wrong with the disk. So I grabbed a CentOS disk and booted up with it and booted into resuce mode and skipped the step that would mount the disks.

Once in I attempted to run fsck /dev/sda1 but I got an error back indicating it did not recognize the volume as EXT2. Searching around I found the following solution if you are using a LVM.

 Perform all of the following commands in the following order. I provided a link for each command if you are interested in knowing more about what each of the commands do.

Scan all disks for partiitions: 

Scan all disks for volume groups and build /etc/lvmtab and /etc/lvmtab.d/* which are the database for all other lvm commands:

Change attributes of a logical volume

Scan all disks for logical volumes

Then I was able to run fsck as follows

fsck -f /dev/VolGroup00/LogVol00

Which of course reported TONS of errors and fixed them up. I do not claim to be an expert but this worked for me. Hopefully this information will help someone else out in the future.

Getting LINQPad to read your applications App.Config settings

I have been using LINQPad for a while now and I love it. This utility allows me to write and execute immediately code that I am working out before I put it into my project. However on occassion I need to reference assemblies from my project that are getting settings from the applications App.Config such as SQL connection strings etc...

Each time I would run into this I would search and not come up with how to get my app.config settings to be honored by LINQPad and today I finally found how to do it so I thought I would write up something really quick to show others how to accomplish this.

1) Find the path that LINQPad uses for its configuration file by excuting this: AppDomain.CurrentDomain.SetupInformation.ConfigurationFile.Dump()

This returned for me the following: C:\Program Files\LINQPad4\LINQPad.config

I was suprised this returned LINQPad.config instead of LINQPad.exe.config which is what you would typically expect since most .NET applications name the file the same as the executable.

2) Take your App.config and copy it to the location above naming the config file whatever yours returned. In my case it was LINQPad.config

3) Close LINQPad or the TAB that you have opened to execute your assembly and reopen to get LINQPad to read the configuration file.

Now execute your code and you should see that LINQPad is now pulling in your settings as needed.

 

Creating custom EventLog source in windows azure

If you have tried to execute the following code on windows azure then no doubt you have run into a security violation. In fact if you try and run the same example on your local machine you will also get the same error as creating an custom event source on windows requires that it be performed by an Administrator.
 
if(!EventLog.SourceExists("MyCustomSource"))
{
	EventLog.CreateEventSource("MyCustomSource", "Application");
}
In order to create a custom event source in windows azure do the following:
 
1) Create a cmd script using notepad with the following content
 
EventCreate /L Application /T Information /ID 900 /SO "MyCustomSource" /D "Custom source for logging my custom events" 
 

Here are the options for EventCreate.exe /?
 EVENTCREATE [/S system [/U username [/P [password]]]] /ID eventid
            [/L logname] [/SO srcname] /T type /D description

Description:
    This command line tool enables an administrator to create
    a custom event ID and message in a specified event log.

Parameter List:
    /S    system           Specifies the remote system to connect to.

    /U    [domain\]user    Specifies the user context under which
                           the command should execute.

    /P    [password]       Specifies the password for the given
                           user context. Prompts for input if omitted.

    /L    logname          Specifies the event log to create
                           an event in.

    /T    type             Specifies the type of event to create.
                           Valid types: SUCCESS, ERROR, WARNING, INFORMATION.

    /SO   source           Specifies the source to use for the
                           event (if not specified, source will default
                           to 'eventcreate'). A valid source can be any
                           string and should represent the application
                           or component that is generating the event.

    /ID   id               Specifies the event ID for the event. A
                           valid custom message ID is in the range
                           of 1 - 1000.

    /D    description      Specifies the description text for the new event.

    /?                     Displays this help message.


2) In your windows azure web role  create a folder called Startup and place the CMD file created above in it
 
3) Right click on the cmd file in solution explorer and select properties and set the "Copy to output directory" to "Copy if newer"
 
 
4) Open the ServiceDefinition.csdef file and add the startup section as shown below. 
 
 
Finished. Now wasnt that simple ;-)
 
Ok, so now you no longer need code that checks if it exists as that will throw a security exception. Now you just write to the EventLog as usual but now using your new custom source.
 
EventLog.WriteEntry("MyCustomSource", "This is my event log message")
 
I also want to give credit to Walter Mayers for pointing out the cmd script approach to creating the custom event source

Installing OpenWebmail on BlueOnyx server

I have been in the process of evaluating the BlueOnyx distribution that replaces the older BlueQuartz web hosting control panel. However one of the short comings is that BlueOnyx does not currently incorporate a webmail interface.

So today I decided to work out what it would take to bring over Openwebmail to the BlueQuartz platform so that as I migrate users to the new platform they will have the same webmail interface that they have been used to. I will also be later installing RoundCube as a second webmail option but for now I want to maintain as much as possible backwards compatibility.

Here is the installation steps:

1) Grab the latest install of openwebmail from: http://openwebmail.acatysmoof.com/

2) Download file to /var/www

3) Extract contents

  • tar -xvzf openwebmail-2.53.tar.gz
  • This will extract files to 2 different directories
    • /var/www/cgi-bin/openwebmail
    • /var/www/data/openwebmail

4) Change permissions on created folders

  • chown root:mail /var/www/cgi-bin/openwebmail
  • chown root:mail /var/www/data/openwebmail

5) Install Text::Iconv perl module required

  • yum install perl-Text-Iconv

6) Add apache config file to set /webmail alias for openwebmail

  • Create file /etc/http/config.d/httpd_openwebmail.conf
  • Place in file the contents of this file: http_openwebmail.conf

 

7) Edit the openwebmail configuration file so that it reflects proper defaults

  • nano -w /var/www/cgi-bin/openwebmail/etc/openwebmail.conf
  • Edit these entries to look as follows.
    • ow_cgidir                         /var/www/cgi-bin/openwebmail
    • ow_cgiurl                         /openwebmail
    • ow_htmldir                        /var/www/data/openwebmail
    • ow_htmlurl                        /data/openwebmail
  • Here is a complete example file that is from my system that reflects not only the above changes but other changes I make from the default to support our current user preferences.
  • openwebmail.conf
    • Note if you enable spell check like my above example configuration make sure you install the aspell library
    • yum install aspell
  • You should open /var/www/cgi-bin/openwebmail/etc/openwebmail.conf.help if you want to understand all the possible settings that can be made

8) Edit dbm.conf

  • nano -w /var/www/cgi-bin/openwebmail/etc/defaults/dbm.conf
  • Change dbm_ext     .db      to      dbm_ext     .pag

9) Restart httpd

  • /etc/init.d/httpd restart

10) Initialize openwebmail by running /var/www/cgi-bin/openwebmail/openwebmail-tool.pl --init

11) Finished - Open your browser and go to one of your websites located on the server and append /webmail to the url and if all is well you will have running webmail on your BlueOnyx machine.

PlayOn Plugin - TECH.ED 2011

Well I really enjoyed creating the MIX 2011 PlayOn plugin that I decided to do the same for TECH.ED 2011. I am going to do a bit more research and see if in the next release I can provide a folder for each year as they have published all of the archives for TECH.ED and MIX back to 2008.

When you access the TECH.ED 2011 channel you will see it grouped by All Sessions - Provides a entire list of all sessions, By Category - groups each of the sessions based on the category it falls under, By Speaker - groups each of the videos by the speaker

TECHED2011.plugin (33.50 kb)

To install download the file and then open your playon software and go to the Plugins tab. At the bottom of that screen click Install and browse out to the file.

**** UPDATE ****

Just updated the download with a new version that supports checking for updates and cached the RSS feed so that subsequent access to the sessions is very quick. 

PlayOn Plugin - MIX 2011

I have been using PlayOn software to view online content via my XBOX for about 6 months now and while it is really cool I dont find myself using it as much as I thought I would. However one thing I have enjoyed is using it to view last years MIX 2010 content that someone provided a plugin script for. However as of MIX 2011 they reformatted the website and now the previous plugin does not work.

So this prompted me to take a look at the PlayOn API and see if I could either fix the script or create a new one. In the process of things I decided to write my own. If you are using PlayOn premium feel free to download and install my plugin to view the MIX 2011 sessions. 

It is truly a joy to sit in front of my TV instead of my computer and watch these sessions. When you access the MIX 2011 channel you will see it grouped by All Sessions - Provides a entire list of all sessions, By Category - groups each of the sessions based on the category it falls under, By Speaker - groups each of the videos by the speaker

MIX2011.plugin (30.50 kb)

To install download the file and then open your playon software and go to the Plugins tab. At the bottom of that screen click Install and browse out to the file.

**** UPDATE ****

Just updated the download with a new version that supports checking for updates and cached the RSS feed so that subsequent access to the sessions is very quick.