Step by Step Beginners Guide to CDF (Community Dashboard Framework)
Dashboards used to be the hottest buzzword among BI enthusiasts such as myself, a Dashboard is like a mini application which gives managers a bird's-eye view of data for the purpose of decision making.
Wikipedia adds that:
“Like a car's dashboard (or control panel), a software dashboard provides decision makers with the input necessary to "drive" the business. Thus, a graphical user interface may be designed to display summaries, graphics (e.g., bar charts, pie charts, bullet graphs, "sparklines," etc.), and gauges (with colors similar to traffic lights) in a portal-like framework to highlight important information.”
In this post we will learn how easy it is to create a Dashboard such as this one:
The Dashboard will allow the user to search for a companies. Companies are arranged in a 3-level hierarchy, and the Dashboard will allow users to navigate down the hierarchy, so if the user clicks a company in one level, the levels below it will show it's subsidiaries and the levels above it will show its parent companies. When ever a company is selected (in any of the levels) the graphs on the bottom display additional data for the company.
To accomplish the task, we are going to use Pentaho BI Server and CDF, which is an open source dashboard framework developed by Pedro Alves from WebDetails.
So first thing first, we have to set up our infrastructure, the good news is that CDF is now an integral part of Pentaho CE, so we just need to download and install Pentaho 3.6:
$ wget http: //sourceforge.net/projects/pentaho/files/Business%20Intelligence%20Server/3.6.0-stable/biserver-ce-3.6.0-stable.tar.gz/download $ tar xzvf biserver-ce-3.6.0-stable.tar.gz $ cd biserver-ce $ ./start-pentaho.sh
Great, you can see that it is working by navigating to port 8080 on the localhost. You can log-in using the default user and password (joe/password), there are many samples, including CDF samples and also CDF documentation is available through the system.
I am going to publish the system on the web so I have to edit tomcat/conf/server.xml and to change the Connector port from 8080 to just 80, and also to edit tomcat/webapps/pentaho/WEB-INF/web.xml and modify the base-url parameter to the url I am going to use.
Next step is to create a datasource to the database, this can be done using the administrator colsole, but for me it is easier to just editing tomcat/webapps/pentaho/META-INF/context.xml and add this:
<Resource name="jdbc/datasource" auth="Container" type="javax.sql.DataSource" factory="org.apache.commons.dbcp.BasicDataSourceFactory" maxActive="20" maxIdle="5" maxWait="10000" username="biserver" password="nottelling" driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/dw"/>
At this point don't forget to restart the server.
OK, next step is to create a solution and a dashboard, the easiest way is copy-paste from the examples.
$ cd pentaho-solutions $ cp -r cdf-samples/20-samples/charts mySolution $ cd mySolution
The solution is defined by the index.xml file and we can edit the index.properties file to give it a name and a description. The Dashboard is defined by the .xcdf file which we should also rename and edit to our own names.
Now you can go back to the web-browser, in order to see the new solution and new Dashboard you have to “refresh the repository cache” (there's a menu option for that and also a refresh icon) and Voilà – a new Dashboard was born.
The content of the dashboard is stored in a template.html file, there are several parts to it which I would explain herein.
First there is the html template itself which defines the structure of the Dashboard. In my case I needed a search box, 3 multi-select-boxes, two bar-charts and one gauge which I am going to layout as follows:
<table align="center" style="border: 0px solid> <tr> <td valign="top">Search:<div id="search_object"></div></td> </tr> <tr> <td valign="top">A Level:</td> <td valign="top">B Level:</td> <td valign="top">C Level:</div></td> </tr> <tr> <td valign="top"><div id="a_object"></div></td> <td valign="top"><div id="b_object"></div></td> <td valign="top"><div id="c_object"></div></td> </tr> <tr> <td valign="top"><div id="channel_object"></div></td> <td valign="top"><div id="dialChart_object"></div></td> <td valign="top"><div id="product_object"></div></td> </tr> </table>
The next step is to create the different components using CDF, the search box is declared using javascript:
var query;
var searchComponent =
{
name: "searchComponent",
type: "textInputComponent",
htmlObject: "search_object",
executeAtStart: true,
parameter: "query",
tooltip: "enter partial company name and press Enter",
postChange: function(){ searchFunction(query); }
}
The htmlObject property is very important, it tells CDF where in the html template the component is going to reside.
The postChange trigger function is going to be called when the users enters a new search query, and would be described in more details later.
Each of the 3 multi-select-boxes is declared in a similar way:
var a;
var aComponent =
{
name: "aComponent",
type: "selectMultiComponent",
parameters:[],
parameter:"a",
size: "10",
valueAsId: false,
htmlObject: "a_object",
executeAtStart: true,
queryDefinition: aQueryDefinition,
listeners: ["event"],
postChange: function(){aFunction(a);}
};
The queryDefinition object, as you must have already gueesed, is responsible for querying the data that is going to populate the component, it will be described in more details below.
Bar-charts are created using this code:
var channelComponent =
{
name: "channelComponent",
type: "jFreeChartComponent",
listeners:["selectedRegionMeasure"],
chartDefinition: channelDefinition,
htmlObject: "channel_object",
executeAtStart: true,
listeners: ["event"]
}
The listeners property lists event names to which this component listens, when ever such event is fired the component will refresh it's data.
Finally the code for a gauge component:
var overallComponent =
{
name: "overallComponent",
type: "dialComponent",
listeners:["departmentMeasure"],
chartDefinition: dialChartDefinition,
htmlObject: "dialChart_object",
executeAtStart: true,
listeners: ["event"]
}
Now that all the components are ready we can go ahead and initiate the Dashboard with this call:
Dashboards.init([searchComponent,aComponent,bComponent,cComponent,overallComponent,channelComponent,productComponent]);
CDF includes many components such as Buttons, Combo-boxes, Menus, Tables, Maps, FlashCharts, Pivot views, Traffic lights and more... check out the online documentation for the full list, descriptions and samples.
The next thing we want to define is the behavior of the Dashboard, in my case the state of the Dashboard is going to be managed using 4 variables and is going to use just one event to refresh all the views when the state changes:
var queryStatus; var aStatus; var bStatus; var cStatus;
Now we can go back and look at the postChange function of the search component, which stores the query and resets any previous state. The call to fireChanges is will dispatch the event through all our views causing it to change.
function searchFunction(val) {
queryStatus = val;
aStatus = "";
bStatus = "";
cStatus = "";
Dashboards.fireChange("event",queryStatus);
}
And for each of the multi-select components, the code is just as simple:
function aFunction(val) {
queryStatus = "";
aStatus = val;
bStatus = "";
cStatus = "";
Dashboards.fireChange("event",queryStatus);
}
Now we are approaching the last part of the development, which is to define the data queries in either SQL or MDX. The query is constructed based on the state of the dashboard.
The queryDefinition object for the multi-selects is:
var aQueryDefinition = {
queryType: 'sql',
jndi: "datasource",
query: function(){
var q= "SELECT distinct A_ID, A_NAME " +
"FROM company " +
"WHERE 1=1 " +
(queryStatus==""?"":("AND A_NAME like '%"+queryStatus+"%' ")) +
(aStatus==""?"":("AND A_ID="+aStatus+" ")) +
(bStatus==""?"":("AND B_ID="+bStatus+" ")) +
(cStatus==""?"":("AND C_ID="+cStatus+" ")) +
"LIMIT 100";
return q;
}
}
As you can see, we are using an SQL query to the datasource to pull a list of all the A level companies that match to the criteria that is defined by the status of the Dashboard, so if the status is a query we will pick only companies whose names contains the search query and if the status is a company we will pick only companies on that branch of the hierarchy.
Similarly, the queryDefinition of the bar-charts is:
var channelDefinition = {
width: 300,
height: 250,
chartType: "BarChart",
datasetType: "CategoryDataset",
is3d: "false",
isStacked: "true",
includeLegend: "false",
foregroundAlpha: 0.7,
queryType: 'sql',
jndi: "datasource",
title: "Channel Revenues:",
query: function(){
var query = "SELECT name, sum(Revenue) " +
"FROM company LEFT JOIN revenue ON company.id=COMPANY_SEQ LEFT JOIN channel ON CHANNEL_SEQ=channel.id " +
"WHERE " +
(aStatus==""&&bStatus==""&&cStatus==""?"1=0 ":"1=1 ") +
(aStatus==""?"":("AND A_ID="+aStatus+" ")) +
(bStatus==""?"":("AND B_ID="+bStatus+" ")) +
(cStatus==""?"":("AND C_ID="+cStatus+" ")) +
"GROUP BY id ORDER BY sum(Revenue) DESC LIMIT 10";
//query = "SELECT 'London',100";
return query;
}
}
This query will return the total revenues by channel of all the companies that are on the branch of the selected company, if no company is selected the query will return no results.
The gauge query will select the average value over all the companies in the branch.
var dialChartDefinition = {
width: 300,
height: 200,
chartType: "DialChart",
queryType: 'sql',
is3d: 'true',
jndi: "datasource",
title: "Overall",
intervals: [40,60,100],
includeLegend: true,
query: function(){
var query = " SELECT avg(ovrall) FROM overall LEFT JOIN company ON COMPANY_SEQ=id "+
" WHERE " +
(aStatus==""&&bStatus==""&&cStatus==""?"1=0":"1=1") +
(aStatus==""?"":("AND A_ID="+aStatus+" ")) +
(bStatus==""?"":("AND B_ID="+bStatus+" ")) +
(cStatus==""?"":("AND C_ID="+cStatus+" "));
//query= "SELECT 75";
return query;
}
}
That would be all. We have defined the Dashboard's view by suppling the html template and the components to populate it, the Dashboard's controller was defined using the components call back functions and the update event and the Dashboard's data model by defining the dynamic queries to the datasource. The Dashboard is ready to be used. Enjoy!

Comments
Hi,
I understand what you explain earlier, but I need to know how is built the template page, because I try to create a new solution under tomcat/ webapps there I copied the folder Char example: tomcat\webapps\pentaho\bi-developers\cdf-samples\20-samples\charts there I have the template but whe I try to access via the url http://localhost:8080/pentaho/bi-developers/cdf-samples/20-samples/chart... nothigs happens, the page is empty...
Can you help me please ???
thanks.
Ps: sorry I don´t speak english...
Sure: The new solution must be created under biserver-ce/pentaho-solutions (and not tomcat/webapps as you have mentioned).
You should access the user's console at http://localhost:8080 (to log in you should select user joe from the drop-down, once in you can select from the menu View->Browser and you will see the list of solutions on the left of the screen)
I have fixed the post, thanks for your comment.
Hi, thanks for your soon reply, but maybe I´m not explaind me well. I know that we have to create the solution under biserver-ce/pentaho-solutions but I just try to use pentaho not in a traditional way...ah just want to use it´s funtionalities because in my solution a don´t have the pentaho user console my loggin only use pentaho security and after I´m going to render to one dashboard. there is when I want to use cdf to create that page..
So the interfaz of pentaho for me doesn´t exists that´s why I sayed before I put my solution under tomcat/webapps/pentaho/my_solution
I read in CDF documnentation we can do this...but I don´t know how.
Extending CDF
With the collaboration of Will Gorman CDF has now a modularized and extensible library of components that can be extended by users. Using an object oriented approach, the key is to extend the class BaseComponent. Here's the simplest component of them all, the TextComponent:
var TextComponent = BaseComponent.extend({ update : function() { $("#"+this.htmlObject).html(this.expression()); } });Having defined this, when CDF reads a component with type="textComponent" it will automatically use the supplied definition. The source code with all the components definition is the best resource for additional information.
thanks in advance!
If what you want is to access the dashboard without the PUC, it can be done by navigating directly to the dashboard through a link such as this one:
http://<hostname>:<port>/pentaho/content/pentaho-cdf/RenderXCDF?solution...
There is no need for extending CDF component or modifing the pentaho webapp.
Hi When i try to acces the samples i get this error if i click on charts:
Found error: [object XMLHttpRequest] - parsererror, Error: undefined
Any idea what this is?
I also was curious what kind of different types u have next to a barchart
For a normal top 5 list for example is the type: text or list?
Which sample are you refering to?
maybe the sample is not fully implemented?
you should also always check for errors on the server side
(see biserver/tomcat/log/catalina.log and biserver/tomcat/bin/pentaho.log)
lumifer, you can find the full list of components in CDF documentation which is published under "BI Developer Examples->CDF->Documentation->Component Reference".
For a simple list you can use a TableComponent or a TextComponent...
I am referring to the cdf samples. (for example the chart samples)
I have followed for server installation this guide:
https://docs.google.com/Doc?docid=0AdJmocc0fj_EZDJ3YmZiZF8xMTBnZ2s2d3Fje...
I am using MSSQL & Windows.
When i look deeper into the xml http parseerror.
The error i get in the pentaho log is:
: Virtual file is not readable: solution:steel-wheels/analysis/SampleData.mondrian.xml
at mondrian.resource.MondrianResource$_Def0.ex(MondrianResource.java:823)
I also saw some solutions on other forums about .xaction files in
C:\Pentaho\biserver-ce-3.6.0-stable\biserver-ce\pentaho-solutions\cdf\components
Where they say the SQL lookup rules are commented out, i adapted this as well.
I have no clue why the examples not working for me.
The error suggests that Pentaho failed to read the xml file,
try to replace the relative path with an absolute path as suggested here
by editing biserver/pentaho-solutions/system/olap/datasources.xml
Hi, Thx for the answer
Still got these errors while adapting full path for the xml file:
I changed it to:
<Catalog name="SteelWheels">
<DataSourceInfo>Provider=mondrian;DataSource=SampleData</DataSourceInfo>
<Definition>C:/Pentaho/biserver-ce-3.6.0-stable/biserver-ce/pentaho-solutions/steel-wheels/analysis/steelwheels.mondrian.xml</Definition>
</Catalog>
ERROR [org.pentaho.platform.plugin.action.mdx.MDXLookupRule] 2a4aeedb-4bc1-11e0-91c8-8b49d5eb3aa5:COMPONENT:context-24081774-1299835383145:jfreechart.xactionMDXConnection.ERROR_0002 - Invalid connection properties: provider=mondrian; Catalog=solution:steel-wheels/analysis/SampleData.mondrian.xml; Role=;
ERROR [org.pentaho.platform.plugin.action.mdx.MDXLookupRule] 2a4b15ec-4bc1-11e0-91c8-8b49d5eb3aa5:COMPONENT:context-8818860-1299835383145:jfreechart.xactionMDXBaseComponent.ERROR_0008 - Connection to data source is not valid
I also saw this 1:
ERROR [org.pentaho.platform.engine.services.solution.SolutionEngine] 2a4b15ec-4bc1-11e0-91c8-8b49d5eb3aa5:SOLUTION-ENGINE:jfreechart.xaction: Action Sequence execution failed, see details below
| Error Time: Friday, March 11, 2011 10:23:05 AM CET
| Session ID: joe
| Instance Id: 2a4b15ec-4bc1-11e0-91c8-8b49d5eb3aa5
| Action Sequence: jfreechart.xaction
| Execution Stack:
EXECUTING ACTION: OLAP (MDXLookupRule)
in IF STATEMENT: QUERY_TYPE == "mdx"
| Action Class: MDXLookupRule
| Action Desc: OLAP
| Loop Index (1-based): 0
I am kinda clueless.
Your help is much appreciated
It seems to be a problem with you database connection, first of all check for the exceptions in the log files, also look at the jndi configuration in tomcat/webapps/pentaho/META-INF/context.xml and at the OLAP data sources at pentaho-solutions/system/olap/datasources.xml
lumifer, if you have additional questions about Pentaho that are not related to this CDF artice, please open a post with your questions. Thanks.
See my post on CDE - The Dashboard Editor
Important notice, in this example the SQL query is prepared on the client side and is sent to the server side for execution,
this approach is dangerous because it allows hackers to send arbitrary (possibly nasty) queries to the database.
The current version of CDF does not longer support direct SQL queries coming from the client, instead, developers are encouraged to use CDA (a server side data access layer)