Wiki

« Back to CatPlugin

CatPlugin - Csharp

About the Plug-in#

The plug-in 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.

You should refer to the Environment Setup instructions to ensure your test environment can properly utilize this plugin.

Creating the Plug-in Project#

1. Open a Command Prompt window.

2. Navigate to the directory you want to use as your workspace. To use the CUAE command-line tool to create a new application, type the cuae create command and the name of the application project.

c:> cd \workspace
c:\workspace> cuae create LolCatPlugin_csharp}}}

The tool then prompts you for additional information.

Tip: Once you are more familiar with the options, you can type the full command rather than answering the prompts by using the following syntax:

cuae create -l language -m namespace -t project type [options] projectname

3. First the tool prompts you to choose between building an application or a plugin. In this example, the correct choice is plugin:

Project Type? [application or plugin] plugin

4. Next the tool prompts you to specify the programming language. In this example, the correct choice is csharp:

Programming language? [java or csharp] csharp

5. The tool asks you for the namespace for the application. A program might want to use a fully qualified namespace like com.company.lolcatplugin_csharp or something simple like lolcatplugin_csharp. The tool offers you a simple namespace based on the project name. Let's accept the tool's suggestion for now and use lolcatplugin_csharp:

Project namespace? [default: <lolcatplugin_csharp>] <Return>

The tool now has enough information to generate the project template.

Generating:

  • plugin named "LolCatPlugin_csharp"
  • with namespace "lolcatplugin_csharp"
  • with language "csharp"
  • in location C:\workspace\

Created project "LolCatPlugin_csharp" in directory "C:\workspace\LolCatPlugin_csharp\"}}}

Here is the whole sequence again with all of the steps together:

c:\workspace> cuae create LolCatPlugin_csharp
Project type? [application or plugin] plugin Programming language? [java or csharp] csharp Project namespace? [default: lolcatplugin_csharp] Generating:

  • plugin named "LolCatPlugin_csharp"
  • with namespace "lolcatplugin_csharp"
  • with language "csharp"
  • in location C:\workspace\

Created project "LolCatPlugin_csharp" in directory "C:\workspace\lolcatplugin_csharp\"}}}

Inspecting the Generated Project#

Let's inspect the files that were generated by the CUAE command-line tool. It created a LolCatPlugin_csharp directory in the directory from which you ran the cuae create command. The LolCatPlugin_csharp directory contains the following files and directories:

  • LolCatPlugin_csharp.etch
  • LolCatPlugin_csharp.properties
  • LolCatPlugin_csharp.csproj
  • cuae-resources/
  • README.txt
  • src/

The most important of which, for your purposes, are:

  • LolCatPlugin_csharp.etch -- CUAE application Etch service definition. Edit this file to use additional CUAE services in your project.
  • LolCatPlugin_csharp.csproj -- C# project file.
  • cuae-resources/ -- CUAE application resource directory.
  • src/ -- Generated application source files.

Declaring Services#

The .etch file in the plugin project contains:

  • a mixin line for the etch bridge service: "mixin cisco.uc.cuae.EtchBridge": This defines the service that the plugin program will consume at run-time for CUAE application management, and should never be modified.
  • Any etch methods the user adds and struct definitions: The server-directioned methods define services that the plugin will implement that can be utilized by other Etch or CUAD applications, and the client-directioned methods define the events that each client application will implement for the plugin to call back for notification.

When a plugin is registered with CUAE and running, the etch application may mixin the plugin's etch file to use the plugin service. A designer applcation may utilize the plugin service by importing the etch file as a reference.

The following snippet is the default content of the .etch file that the CUAE command-line tool generates.

// CUAE Application Etch Service Definition
This service defines your application and the CUAE services that you use in your application.

The service module namespace, this will be translated to the namespace of the generated source code. module lolcatplugin_csharp

The name of your service.service LolCatPlugin_csharp{ By default, you must always use the EtchBridge service. Do not remove this mixin, otherwise your application won't work.mixin cisco.uc.cuae.EtchBridge

You may add any additional service mixins that you require. For example, if you'd like to make and receive phone calls, add the following line: mixin cisco.uc.cuae.legacy.CallControl } }}}

The method "getImageLink()" will be served as a server side API available for CUAE applications (if they are also to mixin this LolCatPlugin_csharp service). The "onImageUpdate()" method will be available on the CUAE application (client) side, which the server may invoke for event notification.Add the following lines before the final "}":

    @Direction( server )
@Timeout( 5000 ) string getImageLink(string sessionId)

@Direction( client ) @Oneway @AsyncReceiver(QUEUED) void triggerOnImageUpdate(string sessionId, string name)}}}

The new LolCatPlugin_csharp.etch file should look like this:

// CUAE Application Etch Service Definition
This service defines your application and the CUAE services that you use in your application.

The service module namespace, this will be translated to the namespace of the generated source code. module lolcatplugin_csharp

The name of your service.service LolCatPlugin_csharp{ By default, you must always use the EtchBridge service. Do not remove this mixin, otherwise your application won't work.mixin cisco.uc.cuae.EtchBridge

You may add any additional service mixins that you require. For example, if you'd like to make and receive phone calls, add the following line: mixin cisco.uc.cuae.legacy.CallControl

@Direction( server ) @Timeout( 5000 ) string getImageLink(string sessionId)

@Direction( client ) @Oneway @AsyncReceiver(QUEUED) void triggerOnImageUpdate(string sessionId, string name) } }}}

Building the Application#

After declaring the services you need, you must build the application to generate source files.

  1. Open a Command Prompt and navigate to the application directory.
  2. Run the msbuild command to generate source files.

c:\workspace\LolCatPlugin_csharp>msbuild

The following source file templates are then created in the directory src\lolcatplugin_csharp:

  • MainLolCatPlugin_csharpListener.cs
  • ImplLolCatPlugin_csharpClient.cs
  • ImplLolCatPlugin_csharpServer.cs

Writing Application Code#

This section provides instructions and code samples for writing the application logic.

Import the Project into your IDE#

  1. Open Visual Studio (or another IDE of your preference).
  2. Create a new CSharp project from the existing project file by selecting File -> Open -> Project/Solution and selecting the LolCatPlugin_csharp.csproj file from the LolCatPlugin_csharp directory.

First example code snippet: ImplLolCatPlugin_csharpServer.cs#

This class polls the RSS feed every ten minutes for updates and refreshes the imagefile when they are found.

Code Sample#

using System;
using System.Threading; using System.Diagnostics;

using Etch.Util; using Metreos.Utilities; using Metreos.LoggingFramework; using cisco.uc.cuae.types.EtchBridge;

using lolcatplugin_csharp.types.LolCatPlugin_csharp;

/<summary>Your custom implementation of BaseLolCatPlugin_csharpServer. Add methods here to provide/implementation of messages from the client. </summary> namespace lolcatplugin_csharp { /<summary>Implementation for ImplLolCatPlugin_csharpServer</summary>public class ImplLolCatPlugin_csharpServer : BaseLolCatPlugin_csharpServer{/ <summary>Constructs the ImplLolCatPlugin_csharpServer.</summary> / <param name="client">a connection to the client session. Use this to/ send a message to the client.</param> public ImplLolCatPlugin_csharpServer(RemoteLolCatPlugin_csharpClient client) { this.client = client; }

/ <summary>A connection to the client session. Use this to/ send a message to the client.</summary> private readonly RemoteLolCatPlugin_csharpClient client;

poll interval: 1 minutes public const int _POLL_INTERVAL = 60000;private DateTime _lastPubDate;private string _lastImgUrl = null;private bool _keepPolling = true;

public override string getImageLink(string sessionId) { Console.WriteLine("ImplCatPluginServer.getImageLink - starts, sessionId is " + sessionId); return MainLolCatPlugin_csharpListener.getCatImageLink(); }

public override void _SessionNotify(Object eventObj) {

Console.WriteLine("ImplCatPluginServer._sessionNotify - starts, event is: " + eventObj); if ("UP".Equals(eventObj)) { Console.WriteLine("ImplCatPluginServer._sessionNotify - Client connected +++++ "); _keepPolling = true; runRssPoll(); return; } else if ("DOWN".Equals(eventObj)) { Console.WriteLine("ImplCatPluginServer._sessionNotify - Client disconnected ----- "); _keepPolling = false; } }

private void runRssPoll() { while (_keepPolling) { checkAndRefreshImgFile(); try { Console.WriteLine("runRssPoll - sleeping 1 minutes before next polling starts..."); Thread.Sleep(_POLL_INTERVAL); } catch (Exception e) { Console.WriteLine("failed to runRssPoll, exception: " + e); } } }

private void checkAndRefreshImgFile() { Console.WriteLine("checkAndRefreshImgFile - starts");

LolCatHandler handler = new LolCatHandler(new LogWriter()); try { handler.downloadAndParseRss(); } catch (Exception e) { Console.WriteLine("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; Console.WriteLine("checkAndRefreshImgFile - shouldUpdate: " + shouldUpdate);

if (!shouldUpdate) return;

try { handler.loadAndSaveImg(); _lastPubDate = handler.getPubDate(); _lastImgUrl = handler.getImgUrl(); }

catch (Exception e) { Console.WriteLine("exception: " + e); }

client.triggerOnImageUpdate("12345", "cat"); } } } }}}

Second example code snippet: ImplLolCatPlugin_csharpClient.cs#

Code Sample#

using System;

using Etch.Util; using cisco.uc.cuae.types.EtchBridge;

using lolcatplugin_csharp.types.LolCatPlugin_csharp;

/<summary>Your custom implementation of BaseLolCatPlugin_csharpClient. Add methods here to provide/implementation of messages from the server. </summary> namespace lolcatplugin_csharp { /<summary>Implementation for ImplLolCatPlugin_csharpClient</summary>public class ImplLolCatPlugin_csharpClient : BaseLolCatPlugin_csharpClient{/ <summary>Constructs the ImplLolCatPlugin_csharpClient.</summary> / <param name="server">a connection to the server session. Use this to/ send a message to the server.</param> public ImplLolCatPlugin_csharpClient(RemoteLolCatPlugin_csharpServer server) { this.server = server; }

/ <summary>A connection to the server session. Use this to/ send a message to the server.</summary> private readonly RemoteLolCatPlugin_csharpServer server;

TODO: Implement delegates or provide implementation of LolCatPlugin_csharpClient messages from the server

public override void _SessionNotify(Object eventObj) { Console.WriteLine("ImplCatPluginClient._sessionNotify - starts, event is: " + eventObj); if ("UP".Equals(eventObj)) { Console.WriteLine("ImplCatPluginClient._sessionNotify - Connect to Etch Bridge"); string uri = MainLolCatPlugin_csharpListener.getCatPluginUri(); Console.WriteLine("getCatPluginURI: " + uri);

Register with the remote server in case it's UP:string key = server.registerPlugin("LolCatPlugin_csharp", "lolcatplugin_csharp.LolCatPlugin_csharp", uri, "<userid>", "<metreos>");Console.WriteLine(" Plugin registered with key: " + key);return;}}

To get a notification if a session endspublic override void sessionEndNotify(String SessionId){Console.WriteLine("SessionID : " + SessionId);}} } }}}

Third example code snippet: LolcatsHandler.cs#

Add a new class LolcatsHandler.cs in the /src directory to handle image download logic. Note that you need to change <web accessible folder location> to a web accessible location that can be written to by your plugin.

Code Sample#

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.ApplicationResource;

using Metreos.LoggingFramework;

using lolcatplugin_csharp.types.LolCatPlugin_csharp;

namespace lolcatplugin_csharp { class LolCatHandler { public LolCatHandler(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); Console.WriteLine("parsed pubDate: " + _pubDate); }

/ Download the RSS xml file and parse it into a DOM object. @throws Exception/public void downloadAndParseRss(){Console.WriteLine("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("http://<proxy-address>.com");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){Console.WriteLine("_rssDoc is null");return;}elseConsole.WriteLine("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){Console.WriteLine("_rssDoc is null");return;}elseConsole.WriteLine("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;

Console.WriteLine("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; Console.WriteLine("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")) { Console.WriteLine("Image url is: " + url); if (_imgUrl == null) _imgUrl = url; } } else if ("pubDate".ToLower().Equals(tag.ToLower())) { string pubDate = elem.FirstChild.Value; if (pubDate != null) setPubDate(pubDate); Console.WriteLine("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){Console.WriteLine("Not image url found in RSS.");}

else { try { HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(_imgUrl); TODO: Update proxy address request.Proxy = new System.Net.WebProxy("http://<proxy-address>.com");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);TODO: Update localhost address img2.Save("<path to save image file. This location should be in your client machine and make this folder accessble to IPPhone. Image will be downloaded to IPPhone from this location.>\lolCatsImg.png"); } catch (Exception e) { Console.WriteLine("Caught exception: " + e); }

} } } } }}}

Registering the Application#

The previous steps completed the core logic of the example applications, but before any Etch-based API calls can actually be made, we must register the application with the Cisco Unified Application Server. To register your application, follow these steps:

1. Open the MainLolCatPlugin_csharpListener file in the /src directory. It contains the following content by default:

using System;

using Etch.Support; using Etch.Util;

using lolcatplugin_csharp.types.LolCatPlugin_csharp;

namespace lolcatplugin_csharp { /<summary>Main implementation for LolCatPlugin_csharpListener.</summary>public class MainLolCatPlugin_csharpListener : LolCatPlugin_csharpHelper.LolCatPlugin_csharpServerFactory{/<summary>Main for LolCatPlugin_csharpListener.</summary> /<param name="args">Command Line Arguments</param>public static void Main(String[] args){ TODO: Change to correct URI string uri = "tcp://localhost:4001";

Transport<ServerFactory> listener = LolCatPlugin_csharpHelper.NewListener( uri, null, new MainLolCatPlugin_csharpListener());

Start the Listenerlistener.TransportControl( TransportConsts.START_AND_WAIT_UP, 4000 );}

/<summary>Return a new instance of LolCatPlugin_csharpServer.</summary>/<param name="client">Reference to remote service</param> /<returns>Server Implementation</returns>public LolCatPlugin_csharpServer NewLolCatPlugin_csharpServer( RemoteLolCatPlugin_csharpClient client ){return new ImplLolCatPlugin_csharpServer( client );}} } }}}

2. To make a connection to the listener, add a server.registerApplication() request. Note in the code below the uri for the EtchBridge listener. It has a KeepAlive timeout setting with each client. If the server detects that the client is not active for a span of time, it will assume it is dead and shut down the connection. Adding in the parameter "TcpTransport.reconnectDelay=4000&filter=KeepAlive" lets the client avoid being shut down by the server in this way.

using System;

using Etch.Support; using Etch.Util;

using lolcatplugin_csharp.types.LolCatPlugin_csharp;

namespace lolcatplugin_csharp { /<summary>Main implementation for LolCatPlugin_csharpListener.</summary>public class MainLolCatPlugin_csharpListener : LolCatPlugin_csharpHelper.LolCatPlugin_csharpServerFactory, LolCatPlugin_csharpHelper.LolCatPlugin_csharpClientFactory{public static string getEtchBridgeUri(){return "tls://<cuaeserver-ip>:9000?TlsConnection.authReqd=false&TcpConnection.reconnect_delay=4000&filter=KeepAlive";}

public static string getCatPluginUri() { client-ip: Where plugin process is runningreturn "tcp://<client-ip>:4001";}

The URL of the generated image file, which can be downloaded by the IP phone. public static string getCatImageLink(){ url to access saved cat image return "http://<IPPhone accessable Web url>/lolCatsImg.png"; } Custom code - ends

/ Main program for CatPluginServer. @param args command line arguments. @throws Exception/public static void Main(String[] args){ TODO Change to correct URI string uri = "tcp://0.0.0.0:4001";

MainLolCatPlugin_csharpListener myInstance = new MainLolCatPlugin_csharpListener();

Transport<ServerFactory> listener = LolCatPlugin_csharpHelper.NewListener(uri, null, myInstance);

Start the Listener

listener.TransportControl(TransportConsts.START_AND_WAIT_UP, 4000); This is also a client of the bridge's listener, init that so that I can call bridge's API for plugin registration or other service later initEtchBridgeServer(myInstance); }

private static void initEtchBridgeServer(MainLolCatPlugin_csharpListener myInstance) { Console.WriteLine("MainLolCatPlugin_csharpListener.initEtchBridgeServer - starts"); string uri = getEtchBridgeUri(); RemoteLolCatPlugin_csharpServer server = LolCatPlugin_csharpHelper.NewServer(uri, null, myInstance); Connect to the service server._StartAndWaitUp(4000);}

public LolCatPlugin_csharpServer NewLolCatPlugin_csharpServer(RemoteLolCatPlugin_csharpClient client) { return new ImplLolCatPlugin_csharpServer(client); }

This listener also needs to implement CatPluginHelper.CatPluginClientFactory in order to init etch bridge server: public LolCatPlugin_csharpClient NewLolCatPlugin_csharpClient(RemoteLolCatPlugin_csharpServer server) { return new ImplLolCatPlugin_csharpClient(server); }

} } }}}

3. You must modify the String URI to match the IP address and port of the Cisco Unified Application Server.

URI settings vary slightly between 2.5(1) Beta 1 and more recent versions of 2.5(1), as follows:

2.5(1) and 2.5(1) Beta 2, Beta 3, Beta 4 & other latest releases#

// TODO Change to correct URI
String uri = "tls://appserver_ipaddress:9000?TlsConnection.authReqd=false&filter=KeepAlive &KeepAlive.Count=5&Packetizer.maxPktSize=102400&TcpTransport.reconnectDelay=4000";}}}

Note: In Beta 2, the Etch Bridge was updated to use Transport Layer Security for encryption by default. For your applications to work, you must specify TLS as the protocol in the URI and set the authReqd parameter to false. In the example above, the KeepAlive filter and Max Packet Size and Reconnect Delay parameters have also been set.

2.5(1) Beta 1#

// TODO Change to correct URI
String uri = "tcp://appserver_ipaddress:9000?&TcpConnection.reconnect_delay=4000";}}}

Note: In addition to setting the correct IP address and port for the Cisco Unfied Application Server, the Reconnect Delay parameter should be set on all connection URIs.

Packaging The Test Application#

  1. Execute a successful build request for your test application within your IDE. If you are getting any build errors then check whether the specified references are added or not.
  2. To package the application, execute the "cuae package" command from a DOS command shell in the parent directory of the test application.

C:\workspace\LolCatPlugin_csharp>cuae package
Created package file "C:\workspace\LolCatPlugin_csharp\bin\LolCatPlugin_csharp.mca"}}}

The following files will be added to the "\bin" sub-directory:

  • LolCatPlugin_csharp.mca (application bundle to be uploaded into the application server)
  • INSTALLER.xml - (contains the config items in config.yaml)
  • MANIFEST.xml - (bundle details for the package comamnd)

Installing The Test Application

  • To install the test application on a CUAE server, execute the cuae install command from a DOS command shell in the parent directory of the test application. You will be prompted to enter an IP address, username, and password (to view the help, run the "cuae install -h" command). When prompted, enter Y or N to save management settings. This will save the answers to the above three questions in the properties file and remember them the next time you go to install.

C:\workspace\LolCatPlugin_csharp>cuae package
Created package file "C:\workspace\LolCatPlugin_csharp\bin\LolCatPlugin_csharp.mca"

C:\workspace\CatPlugin>cuae install Enter management service URI or host/IP <for example: localhost, tcp://1.1.1.1.:4001>: Enter management service login username: Entermanagement service login password: Save the amanagement settings with the project? [yes or no] no Application : C:\workspace\LolCatPlugin_csharp\bin\LolCatPlugin_csharp.mcp Uploading : ===========================> 100% Plugin has been installed successfully}}}

Running the Plugin#

1. Run the class LolCatPlugin_csharp.MainLolCatPlugin_csharpListener to register the application.

At this point the application will remain registered and running until either you stop your application or the application loses connectivity to the Unified Application server.

Adding the Plugin to CUAE#

1. Copy the Etch IDL file LolCatPlugin_csharp.etch to directory %Devtools_HOME%/Framework/1.0/idl/lolcatplugin_csharp/ on the client machine (where CUAE etch application will be developed). This will allow those applications to mixin the LolCatPlugin_csharp service.

Note: Register the plugin by giving exact namespace in "lolcatplugin_csharp.cs" if not your plugin will unable to send the triggering event to applications.eg: lolcatplugin_csharp.LolCatPlugin_csharp should be the namespace matching the cuae.triggerevents parameter value.

2. On your application development machine, open file %Devtools_HOME%/Framework/1.0/cuae.properties, and add "lolcatplugin_csharp.LolCatPlugin_csharp.triggerOnImageUpdate" as a CSV value of the property "cuae.triggerevents" - this will allow the triggering event "lolcatplugin_csharp.LolCatPlugin_csharp.triggerOnImageUpdate" to be a selectable item at application creation time.

logger.level=INFO

  1. The default management port is 9001
  2. cuae.mgmt.port=9001
  1. Put in extra trigger events as comma separated values: they will be added into the
  2. default CUAE trigger event list:

cuae.triggerevents=lolcatplugin_csharp.LolCatPlugin_csharp.triggerOnImageUpdate }}}

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