2 March 2009
Intermediate
LiveCycle ES provides the means to programmatically populate a Flex graph control, such as a Column chart control, that lets you view a visual representation of process data. A LiveCycle ES process can retrieve data from a data source and return XML data to a Flex-based client application. The client application can display data in a control, such as a Column chart control, as shown in the following illustration (see Figure 1).
Notice that a client application makes a request to a LiveCycle ES process. The process retrieves data from a relational database and returns it within an XML document to the client application. The client application parses the data and displays it in a Column chart control. For example, assume that the client application tracks the number of cases that employees within an IT department fix and close. A Column chart control displays the number of cases that each employee closes, as shown in the following illustration (see Figure 2).
For the purposes of this development article, assume that a MySQL data source named employeeData is used. This data source contains a table named employeeTotals, which tracks the number of customer cases that an employee closes (tracked in a field named closedCases). The following table shows the data located within the employeeTotals table.
| emp_id |
emp_name |
closedCases |
|---|---|---|
1 |
Tony Blue |
20 |
2 |
Alex Pink |
20 |
3 |
Steve White |
25 |
4 |
Karen Black |
35 |
Notice that the closedCases field matches the data specified in the Column control shown in the previous illustration. For example, Karen Black has closed 35 cases, which is shown in the Column control. The following XML represents the data that is sent from the LiveCycle ES process to the Flex-based client application.
<root>
<Record>
<Name>Tony Blue</Name>
<Count>20</Count>
</Record>
<Record>
<Name>Alex Pink</Name>
<Count>30</Count>
</Record>
<Record>
<Name>Steve White</Name>
<Count>25</Count>
</Record>
<Record>
<Name>Karen Black</Name>
<Count>35</Count>
</Record>
</root>
Note: For information about creating processes, see LiveCycle Workbench ES Help.
The following table describes the operations in the GetFlexData process.
| Operation | Description |
|---|---|
1 |
Represents the Note: In the Flex client application that is referenced in this document, the |
2 |
Represents a custom component that contains an operation named The Note: Although this document shows the application logic that creates the custom component, it is recommended that you are familiar with creating components. (See "Creating Your First Component" in Programming with LiveCycle ES.) |
To follow along with this development article, create both a MySQL database named employeeData and a table named employeeTotals. Populate the employeeTotals table with the four records shown in the previous table. Make sure that the field names of the employeeTotals table are: emp_id, emp_name, and closedCases. Use the MySQL database that is deployed on the same host as LiveCycle ES. (See Getting Started with MySQL.)
Note: It is not necessary to create a custom control as you can use components available with LiveCycle ES. A custom control is used to query the database and then create XML data that is passed to the Flex-based client application. For information about creating custom components, see "Creating Your First Component" in Programming with LiveCycle ES.
To populate a Column chart control located in a Flex-based client application using LiveCycle ES process data, perform the following steps:
GetEmployeeData. This operation retrieves data from the MySQL data source, dynamically creates XML data, and places the data retrieved from the database within XML data.GetFlexData that returns XML data created by the custom component.GetFlexData process to retrieve XML data. The XML data is used to populate the Column chart control.Create a custom component that queries the MySQL database and constructs XML data that contains the query results. The first step is to create an Eclipse Java project. The version of Eclipse that is supported is 3.2.1 or later. Like other Java projects, add JAR files that your Java business logic depends on to your project's class path. Create an Eclipse project and name it DatabaseComponent. Add the mysql-connector-java-3.1.14-bin.jar file to your project's class path.
To create application logic for your database component, perform the following tasks:
A service implementation is essentially a Plain Old Java Object (POJO) that you can develop by creating a Java interface that defines the service's operations (public methods), input values, and return values. The public methods that are defined within the Java interface become operations that are exposed by the service.
The database component developed in this section exposes an operation named GetEmployeeData. This operation returns an object of type org.w3c.dom.Document. The following example shows the interface that belongs to the database component. Notice that this interface is located within the com.adobe.flexdata.DatabaseComponent package.
Example: Defining the DatabaseComponent interface
package com.adobe.flexdata.DatabaseComponent;
import java.sql.*;
import java.sql.DriverManager.*;
import java.sql.*;
import java.sql.DriverManager.*;
public interface DatabaseComponent {
public org.w3c.dom.Document GetEmployeeData();
}
Create a service implementation class that implements the DatabaseComponent interface. A service implementation must conform to the following restrictions:
Business logic that is executed when an operation is invoked must be specified in the corresponding method. For example, the GetEmployeeData method must contain Java application logic that queries the database and creates XML data that contains the results.
Example: Defining the DatabaseComponentImpl class
package com.adobe.flexdata.DatabaseComponent;
import java.sql.*;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Element;
public class DatabaseComponentImpl implements DatabaseComponent {
//This method builds an XML data source from
//results queried from jdbc:mysql://localhost:3306/employeeData
public org.w3c.dom.Document GetEmployeeData()
{
//Create an org.w3c.dom.Document object
org.w3c.dom.Document document = null;
//Create a String array that contains all employees
String[] allEmployees = new String[5];
allEmployees[0]="Tony Blue";
allEmployees[1]="Alex Pink";
allEmployees[2]="Steve White";
allEmployees[3]="Karen Black";
try{
// Specify an URL to MySQL and
// Create a connection to the database
Class.forName("com.mysql.jdbc.Driver");
String url = "jdbc:mysql://localhost:3306/employeeData";
Connection con = DriverManager.getConnection(url,"root", "root");
// Get a statement object
// This object is used to pass SQL statements to the database
Statement st = con.createStatement();
String tonyTotals = GetEmpCount(st,allEmployees[0]);
String alexTotals = GetEmpCount(st,allEmployees[1]);
String steveTotals = GetEmpCount(st,allEmployees[2]);
String karenTotals = GetEmpCount(st,allEmployees[3]);
//Build the XML data
//Create DocumentBuilderFactory and DocumentBuilder objects
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
//Create a new Document object
document = builder.newDocument();
//Create the root element and append it to the XML DOM
Element root = (Element)document.createElement("root");
document.appendChild(root);
//Create a rec element
Element rec = (Element)document.createElement("Record");
root.appendChild(rec);
Element Name = (Element)document.createElement("Name");
Element Count = (Element)document.createElement("Count");
//Create section for Tony Blue's Totals
Name.appendChild(document.createTextNode(allEmployees[0]));
Count.appendChild(document.createTextNode(tonyTotals));
rec.appendChild(Name);
rec.appendChild(Count);
//Create section for Alex Pink's Totals
Element rec1 = (Element)document.createElement("Record");
root.appendChild(rec1);
Element Name2 = (Element)document.createElement("Name");
Element Count2 = (Element)document.createElement("Count");
Name2.appendChild(document.createTextNode(allEmployees[1]));
Count2.appendChild(document.createTextNode(alexTotals));
rec1.appendChild(Name2);
rec1.appendChild(Count2);
//Create section for Steve White's Totals
Element rec2 = (Element)document.createElement("Record");
root.appendChild(rec2);
Element Name3 = (Element)document.createElement("Name");
Element Count3 = (Element)document.createElement("Count");
Name3.appendChild(document.createTextNode(allEmployees[2]));
Count3.appendChild(document.createTextNode(steveTotals));
rec2.appendChild(Name3);
rec2.appendChild(Count3);
//Create section for Karen Black's Totals
Element rec3 = (Element)document.createElement("Record");
root.appendChild(rec3);
Element Name4 = (Element)document.createElement("Name");
Element Count4 = (Element)document.createElement("Count");
Name4.appendChild(document.createTextNode(allEmployees[3]));
Count4.appendChild(document.createTextNode(karenTotals));
rec3.appendChild(Name4);
rec3.appendChild(Count4);
//Return the XML data that
return document;
}
catch (Exception ee)
{
ee.printStackTrace();
}
return null;
}
//Return the closedCases field value from the employeeTotals table
private String GetEmpCount(java.sql.Statement st,String name)
{
String mt = "";
try{
//Get the single value from the closedCases field
ResultSet rs = st.executeQuery("SELECT closedCases FROM employeeTotals where emp_name = '"+name+"'");
while(rs.next())
mt = rs.getString("closedCases");
}
catch (Exception ee)
{
ee.printStackTrace();
}
return mt;
}
}
Create a component XML file to deploy a component to LiveCycle ES. A component XML file exists for each component and provides metadata about the component. You can use the component XML file to customize your component to meet your requirements. For example, you can specify an icon that is visible to Workbench ES users who build processes by using the component. For information about the schema of a component XML file, see "Component XML Elements" in Programming with LiveCycle ES.
Example: Defining the component XML file for the database component
<component xmlns="http://adobe.com/idp/dsc/component/document">
<!-- Unique id identifying this component -->
<component-id>com.adobe.flexdata.DatabaseComponent</component-id>
<!-- Version -->
<version>1.0</version>
<class-path>mysql-connector-java-3.1.14-bin.jar</class-path>
<!-- Start of the Service definition -->
<services>
<!-- Unique name for service descriptor. The value is used as the default name for deployed services -->
<service name="FlexDatabaseComponent">
<!-- service implementation class definition -->
<implementation-class>com.adobe.flexdata.DatabaseComponent.DatabaseComponentImpl</implementation-class>
<!-- automatically deploys the service and starts it after installation -->
<auto-deploy service-id="FlexDatabaseComponent" />
<operations>
<operation name="GetEmployeeData">
<output-parameter name="outXMLValue" title="outXMLValue" type="org.w3c.dom.Document">
</output-parameter>
<faults>
<fault name="Exception" type="java.lang.Exception"/>
</faults>
</operation>
</operations>
</service>
</services>
</component>
Deploy the component to LiveCycle ES so that you can use it within Workbench ES to build processes. To deploy the component to LiveCycle ES, package your Eclipse project into a JAR file. Ensure that the mysql-connector-java-3.1.14-bin.jar file is included in the JAR file. The component.xml file and external JAR file must be located at the root of the JAR file.
It is recommended that you include only the JAR files that are required to execute the Java application logic located in your component. Although the adobe-livecycle-client.jar file must be in your project's class path to use a com.adobe.idp.Document object, it is not necessary to include this file in the JAR file. This type is part of the LiveCycle ES service container.
Note: Package the database component into a JAR file named adobe-dbSample-dsc.jar. After the database component is packaged into a JAR file, the corresponding CLASS files will also be part of the component JAR file. Without the CLASS files, the component does not work.
To deploy the database component:
Note: You can also deploy the database component programmatically by using the LiveCycle ES API. (See "Programmatically Deploying Components" in Programming with LiveCycle ES.
Create a LiveCycle ES process named GetFlexData that uses the custom component that returns XML data. The process can be a short-lived process, meaning that LiveCycle ES does not create a process instance record. That is, when a Flex client application invokes the process, a record is not created. After the process is created, ensure that you activate it. For information about creating and activating processes, see LiveCycle Workbench ES Help.
Note: The introduction section of this article describes the GetFlexData process. (See GetFlexData process.)
Create a Flex-based client application that invokes the GetFlexData process and display the data in a Column chart control. To invoke the GetFlexData process, use LiveCycle Remoting. For information, see "Invoking LiveCycle ES Using LiveCycle Remoting" in Programming with LiveCycle ES.
You can invoke the GetFlexData process by performing the following steps:
[install_directory]\Adobe\LiveCycle8.2\LiveCycle_ES_SDK\misc\DataServices\Client-Libraries
where [install_directory] is the directory where LiveCycle ES is installed.
mx:RemoteObject instance through either ActionScript or MXML. Ensure that this mx:RemoteObject instance references the GetFlexData process:<mx:RemoteObject id="GetEmpData" destination="GetFlexData" result="resultHandler(event);">
<mx:method name="invoke" result="handleEmployeeReturn(event)"/>
</mx:RemoteObject>
ChannelSet instance to communicate with LiveCycle ES, and associate it with the mx:RemoteObject instance:var cs:ChannelSet= new ChannelSet();
cs.addChannel(new AMFChannel("remoting-amf", "http://" + serverPort + "/remoting/messagebroker/amf"));
GetEmpData.setCredentials("administrator", "password");
GetEmpData.channelSet = cs;
GetFlexData process by calling the mx:RemoteObject instance's invoke method. Ensure that the return value populates an XML object.The following code example is a MXML file that contains Action Script code that invokes the GetFlexData process. Notice that the data source that populates the chart control (named myChart) is named employeeTotals. This data source is an ArrayCollection instance.
Example: A MXML file that contains Action Script code that invokes the GetFlexData process
<?xml version="1.0"?>
<!-- Simple example to demonstrate the ColumnChart controls. -->
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" xmlns="*" creationComplete="SetupColumnChart();">
<mx:Script><![CDATA[
import mx.formatters.NumberFormatter;
import flash.net.FileReference;
import flash.net.URLRequest;
import flash.events.Event;
import flash.events.DataEvent;
import mx.messaging.ChannelSet;
import mx.messaging.channels.AMFChannel;
import mx.rpc.events.ResultEvent;
import mx.collections.ArrayCollection;
import mx.rpc.AsyncToken;
[Bindable]
public var employeeTotals:ArrayCollection = new ArrayCollection();
private function SetupColumnChart():void {
var cs:ChannelSet= new ChannelSet();
cs.addChannel(new AMFChannel("remoting-amf", "http://localhost:8080/remoting/messagebroker/amf"));
GetEmpData.setCredentials("administrator", "password");
GetEmpData.channelSet = cs;
// Invoke the EncryptDocument process
var token:AsyncToken;
token = GetEmpData.invoke();
token.name = name;
}
public function handleEmployeeReturn(event:ResultEvent):void
{
//
var token:AsyncToken = event.token;
var res:Object = event.result;
var myXML:XML = res["outXMLData"] as XML;
//WE need to create application logic to retrieve the values from the
//returned XML data
var myList:XMLList = myXML.children();
var ee:String ="" ;
var index:int = 0 ;
var index2:int = 0;
var employeeName:String ="";
var countRecs:String = "";
for each (var element:XML in myList) {
//trace(element.name());
index=index+1;
//Drill down into the XML further
var myList2:XMLList = element.children();
for each (var element:XML in myList2) {
//THis is where the repeating elements are
if (element.name() == "Name")
{
employeeName = element.toString();
}
if (element.name() == "Count")
{
countRecs = element.toString();
var intNum:int = int(countRecs);
//Populate the datasource
employeeTotals.addItem({Employee: employeeName, Closed_Cases: intNum});
}
}
}
}
private function resultHandler(event:ResultEvent):void
{
// Do anything else here.
}
]]>
</mx:Script>
<mx:RemoteObject id="GetEmpData" destination="GetFlexData" result="resultHandler(event);">
<mx:method name="invoke" result="handleEmployeeReturn(event)"/>
</mx:RemoteObject>
<mx:Panel title="Column Chart">
<mx:ColumnChart id="myChart" dataProvider="{employeeTotals}" showDataTips="true">
<mx:horizontalAxis>
<mx:CategoryAxis dataProvider="{employeeTotals}" categoryField="Employee" />
</mx:horizontalAxis>
<mx:series>
<mx:ColumnSeries xField="Employee" yField="Closed_Cases" displayName="Closed Cases" />
</mx:series>
</mx:ColumnChart>
<mx:Legend dataProvider="{myChart}"/>
</mx:Panel>
</mx:Application>
For more information about creating applications that invoke LiveCycle ES, see Programming with LiveCycle ES.