Wiki

« Back to CatPlugin

CatPlugin - Provider

CatPlugin - Provider Version #

About the Provider#

The provider periodically downloads a JPG image from a URL specified in a well known RSS location, then converts and saves it as a local png file which can be accessed by IP phones via HTTP.

Creating the Provider Project#

A provider is implemented as a C# project. In this example, we will be using Visual Studio for our editing, but you are free to use whatever IDE you are comfortable with.

1. Open Visual Studio, and go to File>New Project. Enter the name LolCatProvider and specify your location. Under templates, select Class Library.

2. We are going to be using two classes in this project, LolCatProvider.cs and LolCatHandler.cs. They have given us one class by default, Class1.cs. Let's rename this to LolCatProvider.cs by R-clicking and selecting Rename. Then add the second class by R-clicking on the project and selecting Add> Class. Specify the name of the new class as LolCatHandler.cs

Writing Provider Code#

This section provides instructions and code samples for writing the provider logic. In this example, we have one main class, LolCatProvider.cs, and one additional class, LolCatHandler.cs.

Example code snippet: LolCatProvider.cs#

Here we define imports, variables, and the constructor of the class LolCatProvider.cs. In it, we declare what libraries we will be using, declare our class with a fully qualified namespace, declare our variables, and implement a constructor.

using System;
using System.Data;
using System.Diagnostics;
using System.Threading;
using Metreos.Core;
using Metreos.Core.ConfigData;
using Metreos.Interfaces;
using Metreos.Messaging;
using Metreos.Utilities;
using Metreos.ProviderFramework; // Contains ProviderBase class
using Metreos.PackageGeneratorCore.Attributes; // Contains attributes for creating Action/Event XML 
                                               // packages for the designer
using Metreos.PackageGeneratorCore.PackageXml; // Some of the arguments of the attributes reference the 
                                               // package XML types directly


namespace Metreos.Providers.LolCatProvider
{
    [ProviderDecl("LolCatProvider")]
    [PackageDecl("Metreos.Providers.LolCatProvider", "Download lolcat from RSS feed")]
    public class LolCatProvider : ProviderBase
    {
        //Event triggerOnImageUpdate
        public const string E1 = "Metreos.Providers.LolCatProvider.triggerOnImageUpdate";
        public const string E1param1 = "name";
     
        //Action getImageLink
        public const string A1 = "Metreos.Providers.LolCatProvider.getImageLink";

        private Thread pollWorker;
        private AutoResetEvent resetter;

        //constructor
        public LolCatProvider(IConfigUtility configUtility) : base (typeof(LolCatProvider), "lolcat provider", configUtility)
        {
            resetter = new AutoResetEvent(false);
        }
     }
}

You need to add the references for the namespaces being used here. For example you need to add a reference to Metreos.Core. Likewise add references to all the namespaces to avoid any compile error.

Example code snippet: LolCatProvider.cs#

The code overrides the following ProviderBase methods:

  • Initialize: Attaches a handler callback to each defined action, invoked when an application executes the specified action.
  • RefreshConfiguration: Resets variable configuration.
  • HandleNoHandler: Executes if no application handles an event it fires.
  • OnStartup: Invokes RegisterNamespace and base.OnStartup. We are also using this method to run the logic of the provider.
  • OnShutdown: Any cleanup the provider needs to do when runtime is shutting down.
protected override bool Initialize(out ConfigEntry[] configItems, out Extension[] extensions)
        {
            this.messageCallbacks.Add(A1, new HandleMessageDelegate(getImageLink));
            configItems = null;
            extensions = null;
            return true;
        }

        protected override void RefreshConfiguration()
        {
            //no config items to refresh
        }
        protected override void HandleNoHandler(ActionMessage noHandlerAction, EventMessage originalEvent)
        {
            log.Write(TraceLevel.Info, "No app to handle event {0}", originalEvent.ToString());
        }

        protected override void OnStartup()
        {
            _keepPolling = true;

            // launch a new thread
            pollWorker = new Thread(new ThreadStart(runRssPoll));
            pollWorker.IsBackground = true;
            pollWorker.Start();

            RegisterNamespace();
            base.OnStartup();
        }

        protected override void OnShutdown()
        {
            _keepPolling = false;
            resetter.Reset();

            base.OnShutdown();
        }

Example code snippet: LolCatProvider.cs#

Define getImageLink and triggerOnImageUpdate and implement provider logic. In the definition of getImageLink, you will need to provide the location where you intend to store the image to be pushed to the phone.

//implement action getImageLink
        [Action(A1, false, "Get Image Link", "returns link of lolcat image", false)]
        [ResultData("url", "url", typeof(string), "url of image")]
        private void getImageLink(ActionBase action)
        {
            log.Write(TraceLevel.Info, "getImageLink initiated. Sessionid: " + action.RoutingGuid);
            action.SendResponse(true, new Field("url", "http://<IPPhone accessable web url to fetch saved image>lolcatsImg.png"));
            
        }

        //implement event triggerOnImageUpdate
        [Event(LolCatProvider.E1, true, null, E1, "New lolCat received")]
        [EventParam(LolCatProvider.E1param1, typeof(string), true, "name")]
        private bool triggerOnImageUpdate(Object eventdata)
        {
            EventMessage msg = CreateEventMessage(LolCatProvider.E1, EventMessage.EventType.Triggering,
                                System.Guid.NewGuid().ToString());

            msg.AddField(LolCatProvider.E1param1, eventdata);
            palWriter.PostMessage(msg);
            return true;
        }

        public static int _POLL_INTERVAL = 600000; 
        private DateTime _lastPubDate; 
        private string _lastImgUrl = null; 
        private bool _keepPolling = true; 

        private void runRssPoll() 
        { 
            while (_keepPolling) { 
                checkAndRefreshImgFile(); 
                try{ 
                    log.Write(TraceLevel.Info, "runRssPoll - sleeping 10 minutes before next polling starts...");
                    resetter.WaitOne(_POLL_INTERVAL, false);

                } catch (Exception e) { 
                    log.Write(TraceLevel.Info, "failed to runRssPoll, exception: " +e); 
                } 
            } 
        } 
         
        private void checkAndRefreshImgFile() 
        { 
            log.Write(TraceLevel.Info, "checkAndRefreshImgFile - starts" ); 
             
            LolcatsHandler handler = new LolcatsHandler(log); 
            try { 
                handler.downloadAndParseRss(); 
            } 
            catch ( Exception e ) { 
                log.Write(TraceLevel.Info, "Failed to download and parse the RSS file: " +e);
                return;
            } 
             
            // Now decide whether we need to refresh the cached image file: 
            bool shouldUpdate = false; 
            if (_lastPubDate == null || _lastImgUrl == null) 
                shouldUpdate = true; 
            else { 
                DateTime pubDate = handler.getPubDate(); 
                string imgUrl = handler.getImgUrl(); 

                if (!_lastImgUrl.Equals( imgUrl )) 
                    shouldUpdate = true; 
                else if (pubDate != null)
                {
                    if (DateTime.Compare(pubDate, _lastPubDate) > 0) 
                        shouldUpdate = true; 
                } 
            } 
            shouldUpdate = true;
            log.Write(TraceLevel.Info, "checkAndRefreshImgFile - shouldUpdate: " +shouldUpdate ); 
             
            if (!shouldUpdate) 
                return; 
             
            try { 
                handler.loadAndSaveImg(); 
                _lastPubDate = handler.getPubDate(); 
                _lastImgUrl = handler.getImgUrl(); 
            } 
            catch ( Exception e ) { 
                log.Write(TraceLevel.Info, "exception: " +e); 
            }

            triggerOnImageUpdate( "cat" ); 
        }

Example code snippet: LolCatHandler.cs#

We have created a separate class to handle downloading and parsing the RSS feed, and then creating and saving the resulting image. (You will need to update the image location in the loadAndSaveImg method. Also note that this code is implemented using a proxy; update the code as you find fit for your own configuration.)

using System;
using System.Collections.Generic;
using System.Text;
using System.Xml;
using System.Net;
using System.Drawing;
using System.IO;
using System.Diagnostics;

using Metreos.LoggingFramework;


namespace Metreos.Providers.LolCatProvider
{
    class LolcatsHandler
    {
        public LolcatsHandler(LogWriter log)
        {
            this.log = log;
        }

        private DateTime _pubDate;
        private string _pageLink = null;
        private string _imgUrl = null;
        private LogWriter log;

        public DateTime getPubDate()
        {
            return _pubDate;
        }

        public string getImgUrl()
        {
            return _imgUrl;
        }

        private void setPubDate(string pubDate)
        {
            if (pubDate == null || pubDate.Trim().Length == 0)
                return;
            pubDate = pubDate.Trim();
            _pubDate = DateTime.Parse(pubDate);
            log.Write(TraceLevel.Info, "parsed pubDate: " + _pubDate);
        }

        /**
         * Download the RSS xml file and parse it into a DOM object.
         * 
         * @throws Exception
         */
        public void downloadAndParseRss()
        {
            log.Write(TraceLevel.Info, "using proxy");
            XmlDocument _rssDoc = new XmlDocument();
            HttpWebRequest request2 = (HttpWebRequest)HttpWebRequest.Create("http://feedproxy.google.com/ICanHasCheezburger");
            //TODO: Update proxy address
            request2.Proxy = new System.Net.WebProxy("<IP Address of the proxy server>");
            request2.Method = "GET";
            request2.Headers.Add(HttpRequestHeader.CacheControl, "max-age=0");
            WebResponse response2 = request2.GetResponse();
            StreamReader reader = new StreamReader(response2.GetResponseStream());
            string lolcatXml = reader.ReadToEnd();
            response2.Close();
            _rssDoc.LoadXml(lolcatXml);
            if (_rssDoc == null)
            {
                log.Write(TraceLevel.Info, "_rssDoc is null");
                return;
            }
            else
                log.Write(TraceLevel.Info, "finished reading RSS file");
            parseRssDoc(_rssDoc);
        }

        /**
         * Parse the DOM object to extract fields for image download.
         * 
         * @throws Exception
         */
        private void parseRssDoc(XmlDocument _rssDoc)
        {
            if (_rssDoc == null)
            {
                log.Write(TraceLevel.Info, "_rssDoc is null");
                return;
            }
            else
                log.Write(TraceLevel.Info, "finished reading RSS file");
            XmlElement rootElem = _rssDoc.DocumentElement;

            XmlNodeList nl = rootElem.GetElementsByTagName("item");
            if (nl == null)
            {
                throw new Exception("No 'item' element is found in the RSS XML");
                return;
            }

            // we are only interested in the very first item: 
            XmlElement firstItem = (XmlElement)(nl.Item(0));
            nl = firstItem.ChildNodes;

            log.Write(TraceLevel.Info, "parsing DOM object...");
            int i = 0;
            while (nl.Item(i) != null)
            {
                XmlNode n = nl.Item(i);
                if (!(n is XmlElement))
                    continue;
                XmlElement elem = (XmlElement)n;
                string tag = elem.Name;
                if ("link".ToLower().Equals(tag.ToLower()))
                {
                    string link = elem.FirstChild.Value;
                    log.Write(TraceLevel.Info, "Found link: " + link);
                    if (_pageLink == null && link.Length > 0)
                        _pageLink = link;
                }
                else if ("media:content".ToLower().Equals(tag.ToLower()))
                {
                    string url = elem.GetAttribute("url");
                    if (url != null && url.EndsWith(".jpg"))
                    {
                        log.Write(TraceLevel.Info, "Image url is: " + url);
                        if (_imgUrl == null)
                            _imgUrl = url;
                    }
                }
                //setPubDate
                else if ("pubDate".ToLower().Equals(tag.ToLower()))
                {
                    string pubDate = elem.FirstChild.Value;
                    if (pubDate != null)
                        setPubDate(pubDate);
                    log.Write(TraceLevel.Info, "pubDate is: " + pubDate);
                }
                i++;
            }
        }

        /**
     * Download the jpg image, and save it as a local png file. The saved
     * file must be HTTP accessible.
     * 
     * @throws Exception
     */
        public void loadAndSaveImg()
        {
            if (_imgUrl == null)
            {
                log.Write(TraceLevel.Info, "Not image url found in RSS.");
            }

            else
            {
                try
                {
                    HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_imgUrl);
                    //TODO: Update proxy address
                    request.Proxy = new System.Net.WebProxy("<IP address of the proxy server>");
                    HttpWebResponse response = (HttpWebResponse)request.GetResponse();
                    Stream stream = response.GetResponseStream();
                    Bitmap img = new Bitmap(stream);
                    Bitmap img2 = new Bitmap(298, 168);
                    Graphics g = Graphics.FromImage(img2);
                    g.DrawImage(img, new Rectangle(0, 0, 298, 168), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel);
                    //TOOD: update image url
                    img2.Save("<path to save image file. Image will be accessible to IPPhones from this location>\lolcatsImg.png");
                }
                catch (Exception e)
                {
                    log.Write(TraceLevel.Info, "Caught exception: " + e);
                }

            }
        }
    }
}

Build and Install the Provider#

Execute a successful build command by R-clicking on the package and selecting uild Solution.

Log in to the CUAE Admin page and click on Plugins. At the bottom there is a form to install a plugin. Click Browse and navigate to LolCatProvider > bin > Debug. Select the file LolCatProvider.dll and click Upload. Your provider should now be listed as Enabled Running.

Tip: To use this provider with a CUAD application, open/create the application in CUAD. In the Explorer window, R-click on the package name and select Add Reference. Navigate to the file LolCatProvider/bin/Debug/LolCatProvider.dll. Now you can use the events and actions specified by LolCatProvider. TriggerOnImageUpdate will be listed as an available trigger when you add a script, and you will be able to use the action getImageLink after R-clicking on the toolbox and selecting Add/Remove Items, checking the getImageLink box, and clicking Ok".

0 Attachments
265 Views
Average (0 Votes)
The average rating is 0.0 stars out of 5.
Comments
No comments yet. Be the first.