CatPlugin - Java - Wiki
Wiki
CatPlugin - Java
About the Plug-in#
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 that can be accessed by IP phones via HTTP
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 plugin, type the cuae create command and the name of the plugin project.
c:> cd \workspace c:\workspace> cuae create CatPlugin
The tool then prompts you for additional information.
Note: 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 java:
Programming language? [java or csharp] java
5. The tool prompts you to choose the build format you prefer. In this example we are using ant.
Build format? [ant or maven2] ant
6. The tool asks you for the namespace for the plugin. A program might want to use a fully qualified namespace like com.company.catplugin or something simple like catplugin. The tool offers you a simple namespace based on the project name. Let's accept the tool's suggestion for now and use catplugin:
Project namespace? [default: <catplugin>] <Return>
The tool now has enough information to generate the project template.
Generating: * plugin named "CatPlugin" * with namespace "catplugin" * with language "java" * in location C:\workspace\ Created project "CatPlugin" in directory "C:\workspace\CatPlugin\"
Here is the whole sequence again with all of the steps together:
c:\workspace> cuae create CatPlugin Project type? [application or plugin] plugin Programming language? [java or csharp] java Build format? [ant or maven2] ant Project namespace? [default: catplugin] Generating: * plugin named "CatPlugin" * with namespace "catplugin" * with language "java" * in location C:\workspace\ Created project "CatPlugin" in directory "C:\workspace\catplugin\"
Inspecting the Generated Project#
Let's inspect the files that were generated by the CUAE command-line tool. It created a CatPlugin directory in the directory from which you ran the cuae create command. The CatPlugin directory contains the following files and directories:
- CatPlugin.etch
- CatPlugin.properties
- build.xml
- cuae-resources/
- README.txt
- src/
The most important of which, for your purposes, are:
- CatPlugin.etch--CUAE application Etch service definition.Edit this file to use additional CUAE services in your project.
- build.xml--Ant build script file with predefined targets for managing the lifecycle of your CUAE project.
- cuae-resources/--CUAE plugin resource directory.
- src/--Generated plugin source files.
Declaring Services#
The .etch file in the plugin project contains:
- Code declaring the Etch Bridge service(mixin cisco.uc.cuae.EtchBridge) that defines the service that the plugin program consumes at runtime for CUAE application management. Do not modify or remove this declaration.
- Any Etch methods the developer adds and struct definitions. The server-directed methods define services that the plugin will implement that can be utilized by other Etch or Cisco Unified Application Designer applications, and the client-directed 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 application 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 catplugin
// The name of your service.
service CatPlugin
{
// 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 Unified Application Environment applications (if declare this CatPlugin service in their own .etch files). The "onImageUpdate()" method will be available on the 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 CatPlugin.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 catplugin
// The name of your service.
service CatPlugin
{
// 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 ant command to generate source files.
c:\workspace\CatPlugin>ant
The following source file templates are then created in the directory src\catplugin:
- MainCatPluginListener.java
- ImplCatPluginClient.java
- ImplCatPluginServer.java
Writing Application Code#
This section provides instructions and code samples for writing the application logic.
Import the Project into your IDE#
1. Open Eclipse (or another IDE of your preference). 2. Create a new Java project from an existing Ant buildfile by selecting File -> New -> Other -> Java Project from an existing Ant buildfile and selecting the build.xml file from the CatPlugin directory.
3. Click Next.
4. Browse to C:\workspace\CatPlugin\ and select the build.xml file.
5. Click Open.
6. Select "javac" task found in target "build [default]
".
First example code snippet: ImplCatPluginServer.java#
This class polls the RSS feed every ten minutes for updates and refreshes the imagefile when they are found.
Code Sample#
package catplugin;
import java.util.Date;
import etch.util.core.io.Session;
/**
* Your custom implementation of BaseCatPluginServer. Add methods here to provide
* implementations of messages from the client.
*/
public class ImplCatPluginServer extends BaseCatPluginServer
{
// poll interval: 1 minutes
public static final long _POLL_INTERVAL = 60000;
private Date _lastPubDate = null;
private String _lastImgUrl = null;
private boolean _keepPolling = true;
/**
* Constructs the ImplCatPluginServer.
*
* @param client a connection to the client session. Use this to send a
* message to the client.
*/
public ImplCatPluginServer( RemoteCatPluginClient client ) {
this.client = client;
}
/**
* A connection to the client session. Use this to send a
* message to the client.
*/
@SuppressWarnings( "unused" )
private final RemoteCatPluginClient client;
// TODO insert methods here to provide implementations of CatPluginServer
// messages from the client.
@Override
public String getImageLink(String sessionId) {
// TODO Auto-generated method stub
System.out.println("ImplCatPluginServer.getImageLink - starts, sessionId is "+sessionId);
return MainCatPluginListener.getCatImageLink();
}
@Override
public void _sessionNotify( Object event ) throws Exception {
System.out.println("ImplCatPluginServer._sessionNotify - starts, event is: " + event);
if(Session.UP.equals( event ))
{
System.out.println("ImplCatPluginServer._sessionNotify - Client connected +++++ ");
//client.triggerNewEevent("12345", "TriggeringEvent");
_keepPolling = true;
runRssPoll();
return;
}
else if(Session.DOWN.equals( event ))
{
System.out.println("ImplCatPluginServer._sessionNotify - Client disconnected ----- ");
_keepPolling = false;
}
}
private void runRssPoll()
{
while (_keepPolling) {
checkAndRefreshImgFile();
try{
System.out.println("runRssPoll - sleeping 1 minutes before next polling starts...");
Thread.sleep( _POLL_INTERVAL );
} catch (Exception e) {
e.printStackTrace( System.out );
}
}
}
private void checkAndRefreshImgFile()
{
System.out
.println( "checkAndRefreshImgFile - starts" );
LolcatsHandler handler = new LolcatsHandler();
try {
handler.downloadAndParseRss();
}
catch ( Exception e ) {
System.out.println( "Failed to download and parse the RSS file:" );
e.printStackTrace( System.out );
}
// Now decide whether we need to refresh the cached image file:
boolean shouldUpdate = false;
if (_lastPubDate == null || _lastImgUrl == null)
shouldUpdate = true;
else {
Date pubDate = handler.getPubDate();
String imgUrl = handler.getImgUrl();
if (!_lastImgUrl.equals( imgUrl ))
shouldUpdate = true;
else if (pubDate != null) {
if (pubDate.after( _lastPubDate ))
shouldUpdate = true;
}
}
shouldUpdate = true;
System.out.println( String.format("checkAndRefreshImgFile - shouldUpdate: %s", shouldUpdate ) );
if (!shouldUpdate)
return;
try {
handler.loadAndSaveImg();
_lastPubDate = handler.getPubDate();
_lastImgUrl = handler.getImgUrl();
if (_lastPubDate == null)
_lastPubDate = new Date();
}
catch ( Exception e ) {
e.printStackTrace( System.out );
}
client.triggerOnImageUpdate( "12345", "cat" );
}
}
Second example code snippet: ImplCatPluginClient.java#
Code Sample#
package catplugin;
import etch.util.core.io.Session;
/**
* Your custom implementation of BaseCatPluginClient. Add methods here to provide
* implementations of messages from the server.
*/
public class ImplCatPluginClient extends BaseCatPluginClient
{
/**
* Constructs the ImplCatPluginClient.
*
* @param server a connection to the server session. Use this to send a
* message to the server.
*/
public ImplCatPluginClient( RemoteCatPluginServer server )
{
this.server = server;
}
/**
* A connection to the server session. Use this to send a
* message to the server.
*/
@SuppressWarnings( "unused" )
private final RemoteCatPluginServer server;
// TODO insert methods here to provide implementations of CatPluginClient
// messages from the server.
@Override
public void _sessionNotify( Object event ) throws Exception
{
System.out.println("ImplCatPluginClient._sessionNotify - starts, event is: "+event);
if(Session.UP.equals( event ))
{
System.out.println("ImplCatPluginClient._sessionNotify - Connect to Etch Bridge");
String uri = MainCatPluginListener.getCatPluginUri();
//Register with the remote server in case it's UP:
String key = server.registerPlugin("CatPlugin", "catplugin.CatPlugin", uri, "<username>", "<metreos>");
System.out.println(" Plugin registered with key: " + key);
return;
}
}
//This is to check that the present session has ended
@Override
public void sessionEndNotify(String SessionId)
{
System.out.println("SessionID : " + SessionId);
}
}
- In the above given code block an additional function sessionEndNotify() is added. It will notify the application when the present session ends.
Third example code snippet: LolcatsHandler.java#
Add a new class LolcatsHandler.java 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.
Confirm that from _RSS_URL specified in the application you are able to download different cat images.
Code Sample#
package catplugin;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.text.SimpleDateFormat;
import java.util.Date;
import javax.imageio.ImageIO;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
public class LolcatsHandler
{
public static final String _RSS_URL = "http://feedproxy.google.com/ICanHasCheezburger";
public static final SimpleDateFormat _DATE_FORMAT = new SimpleDateFormat("EEE, dd MMM yyyy HH:mm:ss Z" );
private Date _pubDate = null;
private Document _rssDoc = null;
private String _pageLink = null;
private String _imgUrl = null;
public Date getPubDate() {
return _pubDate;
}
public String getImgUrl(){
return _imgUrl;
}
private void setPubDate( String pubDate ) {
if (pubDate == null || pubDate.trim().length() == 0)
return;
pubDate = pubDate.trim();
try {
_pubDate = _DATE_FORMAT.parse( pubDate );
}
catch ( Exception e ) {
e.printStackTrace( System.out );
}
}
/**
* Download the RSS xml file and parse it into a DOM object.
*
* @throws Exception
*/
public void downloadAndParseRss() throws Exception {
URL url = new URL( _RSS_URL );
URLConnection conn = url.openConnection();
InputStream instm = conn.getInputStream();
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
_rssDoc = db.parse( instm );
parseRssDoc();
}
/**
* Parse the DOM object to extract fields for image download.
*
* @throws Exception
*/
private void parseRssDoc() throws Exception {
Element rootElem = _rssDoc.getDocumentElement();
NodeList nl = rootElem.getElementsByTagName( "item" );
if (nl == null || nl.getLength() <= 0)
throw new Exception( "No 'item' element is found in the RSS XML" );
// we are only interested in the very first item:
Element firstItem = (Element) (nl.item( 0 ));
nl = firstItem.getChildNodes();
int l = nl.getLength();
for (int i = 0; i < l; i++) {
Node n = nl.item( i );
if (!(n instanceof Element))
continue;
Element elem = (Element) n;
String tag = elem.getTagName();
if ("link".equalsIgnoreCase( tag )) {
String link = elem.getTextContent().trim();
System.out.println( "Found link: " + link );
if (_pageLink == null && link.length() > 0)
_pageLink = link;
}
else if ("media:content".equalsIgnoreCase( tag )) {
String url = elem.getAttribute( "url" );
if (url != null && url.endsWith( ".jpg" )) {
System.out.println( "Image url is: " + url );
if (_imgUrl == null)
_imgUrl = url;
}
}
else if ("pubDate".equalsIgnoreCase( tag )) {
String pubDate = elem.getTextContent();
setPubDate( pubDate );
System.out.println( "pubDate is: " + pubDate );
}
}
}
/**
* Download the jpg image, and save it as a local png file. The saved
* file must be HTTP accessible.
*
* @throws Exception
*/
public void loadAndSaveImg() throws Exception {
if (_imgUrl == null) {
System.out.println( "No image url is found in RSS" );
return;
}
URL url = new URL( _imgUrl );
URLConnection conn = url.openConnection();
InputStream instm = conn.getInputStream();
BufferedImage img = ImageIO.read( instm );
// Re-size it to fit the IP phone's screen
BufferedImage destImg = new BufferedImage( 298, 168,
BufferedImage.TYPE_INT_RGB );
Graphics2D graphics = destImg.createGraphics();
graphics.drawImage( img, 0, 0, 297, 167, 0, 0, img.getWidth()-1, img.getHeight()-1, null );
// Write your logic to make the image available to the IPPhone.
File file = new File("<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>" );
boolean res = ImageIO.write( destImg, "png", file );
System.out.println( "Return value of ImageIO.write() call: " + res );
}
/**
* Test program
*
* @param args
* @throws Exception
*/
public static void main(String[] args) throws Exception {
LolcatsHandler handler = new LolcatsHandler();
handler.downloadAndParseRss();
handler.loadAndSaveImg();
}
}
2. Start the plugin listener and make a connection to the Etch Bridge listener.
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.
package catplugin;
import etch.bindings.java.support.ServerFactory;
import etch.util.core.io.Transport;
/**
* Main program for CatPluginServer. This program makes a listener to accept
* connections from MainCatPluginClient.
*/
public class MainCatPluginListener implements CatPluginHelper.CatPluginServerFactory,
CatPluginHelper.CatPluginClientFactory
{
//Custom code - starts
// This is the IP address of the machine where CUAE is installed.
// If the listener is running on the same CUAE machine, the server & client addresses will be the same
public static String getEtchBridgeUri()
{
return "tls://<cuaeserver-ip>:9000?TlsConnection.authReqd=false&filter=KeepAlive&KeepAlive.Count=5&Packetizer.maxPktSize=102400&TcpTransport.reconnectDelay=4000";
}
public static String getCatPluginUri()
{
// client-ip is where your CatPlugin process is running
return "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 ) throws Exception
{
// TODO Change to correct URI
String uri = "tcp://0.0.0.0:4001";
MainCatPluginListener myInstance = new MainCatPluginListener();
Transport<ServerFactory> listener = CatPluginHelper.newListener( uri, null,
myInstance );
// Start the Listener
listener.transportControl( Transport.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(MainCatPluginListener myInstance) throws Exception
{
System.out.println("MainCatPluginListener.initEtchBridgeServer - starts");
String uri = getEtchBridgeUri();
RemoteCatPluginServer server = CatPluginHelper.newServer( uri, null,
myInstance );
// Connect to the service
server._startAndWaitUp( 4000 );
}
public CatPluginServer newCatPluginServer( RemoteCatPluginClient client )
{
return new ImplCatPluginServer( client );
}
// This listener also needs to implement CatPluginHelper.CatPluginClientFactory
// in order to init etch bridge server:
public CatPluginClient newCatPluginClient( RemoteCatPluginServer server )
throws Exception
{
return new ImplCatPluginClient( 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
// 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. 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\CatPlugin>cuae package
Created package file
"C:\workspace\CatPlugin\bin\CatPlugin.mca" }}}
The following files will be added to the "\bin" sub-directory:
* CatPlugin.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 plugin on a CUAE server, execute the **cuae install** command from a DOS command shell in the parent directory of the plugin. The cuae install command varies slightly between 2.5(1) Beta 1 and Beta 2.
**2.5(1) Beta 2**
You are 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.
**Note:**
// You are also prompted for the communications protocol (TCP or TLS). Select the protocol that is set on the Management Service. TLS is the default supported protocol. If you want to use TCP, follow the instructions in [[ Management Service TLS |Management Service Transport Layer Security (TLS)]] to change the default URI of the Management Service before running these commands.//
{{{
C:\workspace\CatPlugin>cuae package
Created package file
"C:\workspace\CatPlugin\bin\CatPlugin.mca"
C:\workspace\CatPlugin>cuae install
Enter the hostname or IP address of the management service (for example: localhost, 1.1.1.1): localhost
Protocol: tls
Generated mgmt-service uri: tls://localhost:9001?TlsConnection.authReqd=false
Enter management service login username:
Entermanagement service login password: ********
Save the amanagement settings with the project? [yes or no] no
Application : C:\workspace\CatPlugin\bin\CatPlugin.mcp
Uploading : ===========================> 100%
Plugin has been installed successfully
2.5(1) Beta 1
You are 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\CatPlugin>cuae package Created package file "C:\workspace\CatPlugin\bin\CatPlugin.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? [YyNn] n Application : C:\workspace\CatPlugin\bin\CatPlugin.mca Uploading : ===========================> 100% Plugin has been installed successfully
Running the Plugin#
1. Run the class CatPlugin.MainCatPluginListener 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.
Note:
Register the plugin by giving exact namespace in "ImplCatPluginClient.java" if not your plugin will unable to send the triggering event to applications.eg: catplugin.CatPlugin should be the namespace matching the cuae.triggerevents parameter value.
Adding the Plugin to CUAE#
1. Copy the Etch IDL file CatPlugin.etch to directory %Devtools_HOME%/Framework/1.0/idl/catplugin/ on the client machine (where CUAE etch application will be developed). This will allow those applications to mixin the CatPlugin service.
2. On your application development machine, open file %Devtools_HOME%/Framework/1.0/cuae.properties, and add "catplugin.CatPlugin.triggerOnImageUpdate" as a CSV value of the property "cuae.triggerevents" - this will allow the triggering event "catplugin.CatPlugin.triggerOnImageUpdate" to be a selectable item at application creation time.
logger.level=INFO # The default management port is 9001 #cuae.mgmt.port=9001 #Put in extra trigger events as comma separated values: they will be added into the #default CUAE trigger event list: cuae.triggerevents=catplugin.CatPlugin.triggerOnImageUpdate