Monday, August 6, 2012

Polycom Statistics Application: How I made it !!

Polycom has delivered new APIs that you can leverage to developp custom applications. In previous articles, I have described two type of applications one for End-Users contextually integrated within their UC client (IBM Sametime) and one for Video Administrator to have a view of their video infrastructure workload.

I would like to share with you some details about the way I have designed the second application: Polycom Statistics Application.

That application only uses these technologies:
  • Polycom DMA V5
  • IBM WebSphere Application Server 7.0 (J2EE Server)
  • IBM DB2 Database
  • Java SDK 1.5/1.6
  • Google Charts
Why did I use these solutions:

- IBM Websphere Application server: It provide a scheduler which simplifies the deployment of scheduled servlet. In my case, the application pulls Polycom DMA information regarding RMX audio/video usage every x minutes. Open-source J2EE server doesn't have a scheduler, out of the box (or I'm not aware of). One solution could be to implement Quartz Scheduler (here).
Otherwise, you can download a trial server of WebSphere Application Server (here).

- DB2 Database: It can be replaced very easily or you can use DB2 Express-C.
DB2 Express-C is the free edition of the IBM DB2 database server. You can download and install DB2 Express-C on your own machine.

- Google Charts: I was looking for an easy solution to create charts (Gauge, Line Charts) in a web page. Lots of solution exist in the market, Ajax framework (like Dojo) also provides these kind of features. But, I was very surprised by the simplicity to ue Google Charts. More details here.

- Java: I have to confess, I'm not a developper,and I just understand Java... But, it could be done in ASP.Net, PHP, ....

Polycom APIs
Before you can start developing applications with Polycom API, you need to understand how it works. The API works over HTTPS and expects an HTTPS request to a designated endpoint. On receipt of this request, the API server replies to the query with a XML feed containing the requested data. It's then possible to parse this data using either a server-side programming language (such as PHP, Perl or Java - which is my case) or a client-side toolkit (such as jQuery or Dojo) and extract content from it for integration into a web page.

Each data object accessible through the API is modeled as a resource. Some example resources are users, conference rooms, conferences, and conference participants. Each resource is uniquely identified by a URL and is referenced by means of the URL path.

Polycom has written a real good guide to start using these APIs named Polycom® RealPresence Platform™ Application Programming Interface (API) Developer Guide.

In my application, I just used one API:
https://[server hostname]:8443/api/rest/mcus

The response body of that method is something like that:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<ns4:plcm-mcu-list xmlns="urn:com:polycom:api:rest:plcm-mcu-capacity" xmlns:ns2="http://www.w3.org/2005/Atom" xmlns:ns3="urn:com:polycom:api:rest:plcm-mcu"
xmlns:ns4="urn:com:polycom:api:rest:plcm-mcu-list">
  <plcm-mcu-capacity>
    <mcu-card-type>mpm</mcu-card-type>
    <total-audio-ports>0</total-audio-ports>
    <total-video-ports>0</total-video-ports>
  </plcm-mcu-capacity>
  <ns3:plcm-mcu>
    <ns2:link title="Containing MCU Pool"
type="application/vnd.plcm.plcm-mcu-pool+xml"
rel="urn:com:polycom:api:rest:link-relations:containing-mcu-pool"
href="https://server:port/api/rest/mcu-pools/1"/>
    <ns2:link title="Self Relationship"
type="application/vnd.plcm.plcm-mcu+xml" rel="self"
href="https://server:port/api/rest/mcus/30304b0b-af46-48e3-a69c-aa5c
6554f8b8"/>
    <plcm-mcu-capacity>
      <mcu-card-type>mpm</mcu-card-type>
      <total-audio-ports>0</total-audio-ports>
      <total-video-ports>0</total-video-ports>
    </plcm-mcu-capacity>
  
<ns3:mcu-identifier>30304b0b-af46-48e3-a69c-aa5c6554f8b8</ns3:mcu-id
entifier>
    <ns3:name>mcu1</ns3:name>
    <ns3:management-ip>10.47.17.121</ns3:management-ip>
    <ns3:mcu-type>RMX</ns3:mcu-type>
    <ns3:overlap>false</ns3:overlap>
    <ns3:reserved-audio-ports>0</ns3:reserved-audio-ports>
    <ns3:reserved-video-ports>0</ns3:reserved-video-ports>
    <ns3:dma-audio-ports>0</ns3:dma-audio-ports>
    <ns3:dma-video-ports>0</ns3:dma-video-ports>
    <ns3:dma-used-audio-ports>0</ns3:dma-used-audio-ports>
    <ns3:dma-used-video-ports>0</ns3:dma-used-video-ports>
    <ns3:total-used-audio-ports>5</ns3:total-used-audio-ports>
    <ns3:total-used-video-ports>10</ns3:total-used-video-ports>
    <ns3:max-bit-rate>2048</ns3:max-bit-rate>
    <ns3:entity-tag>6f7e56fbb1d6c0b01c787b4976135053</ns3:entity-tag>
  </ns3:plcm-mcu>
</ns4:plcm-mcu-list>

In that example, the Polycom DMA only controls one RMX ("mcu1") and you can see that currently, the Polycom RMX is using 5 audio ports and 10 video ports.
My application retrieves that XML feed, parses its content and stores in the database these two info.

Retrieving XML feeds via REST APIs

Using Java, I always use the same method to retrieve XML documents. Only the URL can changed in our case. You can reuse it as-is in your Java code.

public static String RetrieveStats(String URI) throws IOException {
        StringBuilder sb=null;
        try{
            URL url = new URL(URI);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
           
            //Login & Password Base64-encoded
            String userpass_encode = "YWRtaW46YWRtaW4="; 
            String basicAuth = "Basic " + userpass_encode;
              conn.setRequestProperty ("Authorization", basicAuth);
            if (conn.getResponseCode() != 200) {
                throw new IOException(conn.getResponseMessage());
              }
              // Buffer the result into a string
              BufferedReader rd = new BufferedReader(
                  new InputStreamReader(conn.getInputStream()));
              sb = new StringBuilder();
              String line;
              while ((line = rd.readLine()) != null) {
                sb.append(line);
              }
              rd.close();
              conn.disconnect();
              }
                catch (MalformedURLException e) {
                        e.printStackTrace();
                } 
              return sb.toString();
            }
    } 

Parsing XML feeds:
Multiple solutions exist to parse a XML feed in Java. Personally, I have used standard Java classes allowing to parse XML feed: javax.xml.parsers
The javadoc can be found here.

Document doc = loadXMLFromString(str);
doc.getDocumentElement ().normalize ();
NodeList listOfPersons = doc.getElementsByTagName("ns3:plcm-mcu");
int totalMCUs = listOfPersons.getLength();
   for(int s=0; s<listOfPersons.getLength() ; s++){
      Node firstPersonNode = listOfPersons.item(s);
      if(firstPersonNode.getNodeType() == Node.ELEMENT_NODE){
          Element firstPersonElement = (Element)firstPersonNode;
 //ID of the MCU
         NodeList firstNameList = firstPersonElement.getElementsByTagName("ns3:name");
         Element firstNameElement = (Element)firstNameList.item(0);
         NodeList textFNList = firstNameElement.getChildNodes();
         str_final[s][0] = (String)((Node)textFNList.item(0)).getNodeValue().trim() 
 .......... 


Important Method in that code: getElementsByTagName()
That method returns a list of all the XML Elements with a given tag name.

In that piece of code, I retrieve the value in the XML element "ns3:name" mcu1.
I also retrieved the content of  "ns3:plcm:mcu" because, a Polycom DMA can manage multiple RMXes.

Using POST method: 
This application only uses GET method, but of course, the APIs allow to send informations to the Polycom infrastructure, for example to add participants to a conference or to schedule a conference...

The code is very similar, except you have to create a XML feed (in your application) with proper informations.
In that listing, the XML Body allows you to add a participants to an existing meeting.
I used this POST method: https://localhost:8443/api/rest/conferences/{conference-identifier}/participants

This method creates and initiates a dial out to a new conference participant. Currently the only elements in plcm-participant that have any effect for dial out are: endpoint-number, passback, and passthru.

The XML body is highlight in Green of my piece of code, and the variable in red. In my case, I just filled in the "endpoint number" which is the h323 or SIP uri of the endpoint.

HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
conn.setAllowUserInteraction(false);
String userpass = "admin:admin";
String basicAuth = "Basic " + new String(Base64.encode(userpass.getBytes()));
conn.setRequestProperty ("Authorization", basicAuth);
conn.setRequestProperty("Content-Type","application/vnd.plcm.plcm-participant+xml");   
 
// XML Body
  
String xml_Header = "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\r\n" + 
          "<ns3:plcm-participant xmlns=\"http://www.w3.org/2005/Atom\" xmlns:ns3=\"urn:com:polycom:api:rest:plcm-participant\">\r\n";
          String message =
                  "<ns3:participant-identifier>286a2d42-6531-001f-0c83-00397edf2cf5</ns3:participant-identifier>\r\n" +
                  "<ns3:display-name>vperrin</ns3:display-name>\r\n" +
                  "<ns3:endpoint-identifier>vperrin</ns3:endpoint-identifier>\r\n" +
                  "<ns3:endpoint-name>vperrin</ns3:endpoint-name>\r\n" +
                  "<ns3:endpoint-number>" +
                  telNumber +
                  "</ns3:endpoint-number>\r\n" +
                  "<ns3:conference-identifier>dma7000:5.0.0.87280:dma812555</ns3:conference-identifier>\r\n" +
                  "<ns3:mcu-name>10.223.43.224</ns3:mcu-name>\r\n" +
                  "<ns3:connection-status>CONNECTED_DIAL_IN</ns3:connection-status>\r\n" +
                  "<ns3:chairperson>false</ns3:chairperson>\r\n" +
                  "<ns3:lecturer>false</ns3:lecturer>\r\n" +
                  "<ns3:audio-mute>false</ns3:audio-mute>\r\n" +
                  "<ns3:video-mute>false</ns3:video-mute>\r\n" +
                  "<ns3:signaling-protocol>H323</ns3:signaling-protocol>\r\n" +
                  "<ns3:encrypted-media>false</ns3:encrypted-media>\r\n" +
                  "<ns3:entity-tag>38de82d0af22fbd20709c85dc80245a4</ns3:entity-tag>\r\n" +
                  "<ns3:connection-start-time>2012-04-29T04:11:13.147-06:00</ns3:connection-start-time>\r\n" +
              "</ns3:plcm-participant>\r\n";
      
// End of XML Body
  
          String data = xml_Header + message;
          System.out.println(data);
         
          // Create the form content
          OutputStream out = conn.getOutputStream();
          Writer writer = new OutputStreamWriter(out, "UTF-8");
            writer.write(xml_Header);
            writer.write(message);
          writer.close();
          out.close();
                   
          if (conn.getResponseCode() != 200) {
              System.out.println("ERROR");
              InputStream is= conn.getErrorStream();
            //read it with BufferedReader
                BufferedReader br = new BufferedReader(new InputStreamReader(is));
                StringBuilder sb = new StringBuilder();
                String line;
                while ((line = br.readLine()) != null) {
                    sb.append(line);
                }
                System.out.println(sb.toString());
   
                br.close();
             
            throw new IOException(conn.getResponseMessage());
          }

          conn.disconnect();   
          return data;
        }   
}


As you can see, the code is really simple but also generic... You can now imagine to build custom applications using Polycom APIs in Java or in other language...