java.net member

Rechercher dans ce site

Drawing Charts on Google App Engine for Java using JSF2 and Google Visualization API

>> 16 November 2010




On Google App Engine, many of the classes used in Java for drawing or displaying images are restricted classes (Not on GAEJ white list). A partial solution is to use Google's dedicated Images service. Another solution is to use for instance another framework for images with permitted classes. Google has an elegant library for visualizing data, like charts among other interesting visualizations. Using this visualization API is pretty straightforward, here is a step by step example

How to use Google Visualization API

  • Begin by loading some Javascript libraries, there are 3 to load.
    1. Google AJAX API
      This library must be loaded first.
      Is used to load other libraries and handle some core functionality such as event handling.
      Defines the google.load() and google.setOnCallback()
      <script type="text/javascript" src="http://www.google.com/jsapi"></script>


    2. Google Visualization API core
      Common classes and methods used to create and handle visualizations (DataTable, query object, error handlers),
      All is in google.visualization namespace.
      Loading this library is combined with the third one.

    3. A library for each type of visualization
      Each one is a specific visualization (for example, a pie chart or a line chart)
      Is a JavaScript class that exposes methods specific to that visualization, plus a few common methods and events (such as draw() and select).
      <script type="text/javascript">
      // Load the Visualization API and the piechart package.
      google.load('visualization', '1', {'packages':['corechart']});

  • Prepare your data.
    specifying the data yourself in code,
    or querying a remote site for data.

  • Create an instance of your visualization.
    Instantiate your visualization by calling its constructor and passing in a reference to your <div> element (see later).

  • Draw your visualization.
    Call draw() on your visualization and pass in your data to draw your visualization on the page.

  • Options.
    There are various other optional actions, such as handling user events or specifying visualization options.

  • Add a <div> to your page.
    This is the visualization area in the page
A simple HTML Example:

<html>
<head>
<!--Load the AJAX API-->
<script type="text/javascript" src="http://www.google.com/jsapi"></script>


<script type="text/javascript">

// Load the Visualization API and the piechart package.
google.load('visualization', '1', {'packages':['corechart']});



// Set a callback to run when the Google Visualization API is loaded.
google.setOnLoadCallback(drawChart);


// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.

function drawChart() {

// Create our data table.
var data = new google.visualization.DataTable();

//the first col is a String the second is a number
data.addColumn('string', 'Month');
data.addColumn('number', 'Users');

//we pass a 2 dimension array of (string,number)
data.addRows([
['jan', 11],
['feb', 2],
['mar', 12],
['apr', 20],
['jun', 7]
]);




// Instantiate and draw our chart, passing in some options.

var chart = new google.visualization.PieChart(document.getElementById('chart_div'));


//first arg to draw is an instance of DataTable, the second are options

chart.draw(data, {width: 400, height: 240, is3D: true, title: 'Users'});
}
</script>

</head>

<body>
<!--Div that will hold the pie chart-->
<div id="chart_div"></div>
</body>
</html>

JSF2 combined with Visualization API
First example
A very simple rating, using radio buttons

SiteStatBean (SiteStatBean.java)
package com.java_javafx.ka.visualization;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

/**
* @author Kaesar ALNIJRES
*
*/
@ManagedBean(name="stat")
@SessionScoped

public class SiteStatBean  implements Serializable{
private int good=1;
private int average;
private int bad;
private String rating;
private List<SelectItem> ratingChoices;

/**
* @return the good
*/
public int getGood() {

return good;
}
/**
* @param good the good to set
*/
public void setGood(int good) {
this.good = good;
}
/**
* @return the average
*/
public int getAverage() {
return average;
}
/**
* @param average the average to set
*/
public void setAverage(int average) {
this.average = average;
}
/**
* @return the bad
*/
public int getBad() {

return bad;
}
/**
* @param bad the bad to set
*/
public void setBad(int bad) {
this.bad = bad;
}
/**
* @return the rating
*/
public String getRating() {

return rating;
}
/**
* @param rating the rating to set
*/
public void setRating(String rating) {
String selected=rating;

if(selected.equals("Good"))
good++;
if (selected.equals("Average"))
average++;
if (selected.equals("Bad"))
bad++;

this.rating = rating;
}
/**
* @return the ratingChoices
*/
public List<SelectItem> getRatingChoices() {

ratingChoices=new ArrayList<SelectItem>();
ratingChoices.add(new SelectItem("Good") );
ratingChoices.add(new SelectItem("Average") );
ratingChoices.add(new SelectItem("Bad") );

return ratingChoices;
}
/**
* @param ratingChoices the ratingChoices to set
*/
public void setRatingChoices(List<SelectItem> ratingChoices) {
this.ratingChoices = ratingChoices;
}


}

polling.xhtml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Polling</title>
<!-- Using Google Visualization API -->

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">

var loaded=false;
var chart;
var dataTable;
google.load('visualization', '1', {'packages':['corechart']});

google.setOnLoadCallback(drawChart);



// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.

function drawChart() {
loaded=true;

// Create our data table.
dataTable = new google.visualization.DataTable();

//the first col is a String the second is a number
dataTable.addColumn('string', 'Site');
dataTable.addColumn('number', 'Rating');

//we pass a 2 dimension array of (string,number)

dataTable.addRows([
['Good', #{stat.good}],
['Average', #{stat.average}],
['Bad', #{stat.bad}],

]);



// Instantiate and draw our chart, passing in some options.

chart = new google.visualization.PieChart(document.getElementById('polling_div'));


//first arg to draw is an instance of DataTable, the second are options

chart.draw(dataTable, {width: 400, height: 240, is3D: true, title: 'Rating for this site'});

}


</script>
<!-- end visualization api -->
</head>
<body>


<h:form>
<h:panelGrid>

<h:outputText value="What do you think of this site?" />
<div id="polling_div"></div>
<h:selectOneRadio value="#{stat.rating}" id="rating">
<f:ajax render="panel"/>
<f:selectItems value="#{stat.ratingChoices}" />
</h:selectOneRadio>

</h:panelGrid>
</h:form>

<h:panelGrid id="panel">
<script type="text/javascript">
if(loaded)
{
var good=#{stat.good}
var average=#{stat.average}
var bad=#{stat.bad}

dataTable.setValue(0,1,good)
dataTable.setValue(1,1,average)
dataTable.setValue(2,1,bad)

chart.draw(dataTable, {width: 400, height: 240, is3D: true, title: 'Rating for this site'});
}
</script>

</h:panelGrid>


</body>
</html>

Visualization API with JSF2


Example 2
Adding a progress bar (busy bar)
First of all we add a function in JavaScript section, this function will be called when an Ajax event is launched. It received an argument "data", or any other name. The variable "data.status" gives information about the progression of the Ajax request. If this variable's value is "begin",the request is sent and the program is waiting for the response. In this case we display any image to let the user wait. If the variable has any other value like "success" or "complete" the request has ended, so we can hide the busy image and display the chart. Javascript function is a callback function triggered by "onevent" attribute in the Ajax call.

I've added a sleep call, only to simulate long call on the development server

Note:
prependId="false" of the form is used to split components ID from the form's ID. Like this we can the given ids without using the form's associated id.

polling2.xhtml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
xmlns:f="http://java.sun.com/jsf/core"
xmlns:h="http://java.sun.com/jsf/html">

<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<title>Polling</title>
<!-- Using Google Visualization API -->

<script type="text/javascript" src="http://www.google.com/jsapi"></script>
<script type="text/javascript">

var loaded=false;
var chart;
var dataTable;
google.load('visualization', '1', {'packages':['corechart']});

google.setOnLoadCallback(drawChart);



// Callback that creates and populates a data table,
// instantiates the pie chart, passes in the data and
// draws it.

function drawChart() {
loaded=true;

// Create our data table.
dataTable = new google.visualization.DataTable();

//the first col is a String the second is a number
dataTable.addColumn('string', 'Site');
dataTable.addColumn('number', 'Rating');

//we pass a 2 dimension array of (string,number)

dataTable.addRows([
['Good', #{stat.good}],
['Average', #{stat.average}],
['Bad', #{stat.bad}],

]);



// Instantiate and draw our chart, passing in some options.

chart = new google.visualization.PieChart(document.getElementById('polling_div'));


//first arg to draw is an instance of DataTable, the second are options

chart.draw(dataTable, {width: 400, height: 240, is3D: true, title: 'Rating for this site'});

}
function waiting(data)
{

var status=data.status;
var component = document.getElementById("polling_div");
var progressBar=document.getElementById("progressbar");

if (status == "begin") {
component.style.visibility = "hidden";
progressBar.style.visibility="visible";
}
else {
component.style.visibility = "visible";
progressBar.style.visibility="hidden";

}

}

</script>
<!-- end visualization api -->
</head>
<body>


<h:form prependId="false">
<h:panelGrid>


<h:outputText value="What do you think of this site?" />
<h:panelGroup>
<h:graphicImage id="progressbar" url="/images/load.gif" style="visibility:hidden"/>
<div id="polling_div"></div>
</h:panelGroup>
<h:selectOneRadio value="#{stat.rating}" id="rating">
<f:ajax render="panel" onevent="waiting"/>
<f:selectItems value="#{stat.ratingChoices}" />
</h:selectOneRadio>

</h:panelGrid>
</h:form>

<h:panelGrid id="panel">
<script type="text/javascript">
if(loaded)
{
var good=#{stat.good}
var average=#{stat.average}
var bad=#{stat.bad}

dataTable.setValue(0,1,good)
dataTable.setValue(1,1,average)
dataTable.setValue(2,1,bad)

chart.draw(dataTable, {width: 400, height: 240, is3D: true, title: 'Rating for this site'});
}
</script>

</h:panelGrid>


</body>
</html>

MangedBean (SiteStatBean.java)

package com.java_javafx.ka.visualization;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

/**
* @author Kaesar ALNIJRES
*
*/
@ManagedBean(name="stat")
@SessionScoped

public class SiteStatBean  implements Serializable{
private int good=1;
private int average;
private int bad;
private String rating;
private List<SelectItem> ratingChoices;

/**
* @return the good
*/
public int getGood() {

return good;
}
/**
* @param good the good to set
*/
public void setGood(int good) {
this.good = good;
}
/**
* @return the average
*/
public int getAverage() {
return average;
}
/**
* @param average the average to set
*/
public void setAverage(int average) {
this.average = average;
}
/**
* @return the bad
*/
public int getBad() {

return bad;
}
/**
* @param bad the bad to set
*/
public void setBad(int bad) {
this.bad = bad;
}
/**
* @return the rating
*/
public String getRating() {

return rating;
}
/**
* @param rating the rating to set
*/
public void setRating(String rating) {
String selected=rating;

if(selected.equals("Good"))
good++;
if (selected.equals("Average"))
average++;
if (selected.equals("Bad"))
bad++;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
this.rating = rating;
}
/**
* @return the ratingChoices
*/
public List<SelectItem> getRatingChoices() {

ratingChoices=new ArrayList<SelectItem>();
ratingChoices.add(new SelectItem("Good") );
ratingChoices.add(new SelectItem("Average") );
ratingChoices.add(new SelectItem("Bad") );

return ratingChoices;
}
/**
* @param ratingChoices the ratingChoices to set
*/
public void setRatingChoices(List<SelectItem> ratingChoices) {
this.ratingChoices = ratingChoices;
}


}



Ajax request began.Waiting

Ajax request has finished

Example 3
Persist our data
To persist the data, I'll use some JDO annotations in the ManagedBean to persist and retrieve data from the Datastore. A method "init" annotated "@PostConstruct" is called only once, after the construction of the bean's object. PMF is a singleton used to get a PersistenceManagerFactory instance. A better example for persisting would be for instance to add a button and verify the user's IP, instead of persisting and updating as a response to selecting <h:selectOneRadio.

PMF.java
package com.java_javafx.ka.visualization;

import javax.jdo.JDOHelper;
import javax.jdo.PersistenceManagerFactory;

public final class PMF {
private static final PersistenceManagerFactory pmfInstance =
JDOHelper.getPersistenceManagerFactory("transactions-optional");

private PMF() {}

public static PersistenceManagerFactory get() {
return pmfInstance;
}
}

ManagedBean(SiteStatBean.java)

package com.java_javafx.visualization;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;

import javax.annotation.PostConstruct;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.model.SelectItem;
import javax.jdo.PersistenceManager;
import javax.jdo.PersistenceManagerFactory;
import javax.jdo.annotations.*;
import com.google.appengine.api.datastore.Key;
/**
 * @author Kaesar ALNIJRES
 *
 */
@PersistenceCapable(identityType = IdentityType.APPLICATION)
@ManagedBean(name="stat")
@SessionScoped

public class SiteStatBean implements Serializable{
    @PrimaryKey
    @Persistent(valueStrategy = IdGeneratorStrategy.IDENTITY)
    private Key key;
@Persistent
private int good;
@Persistent
private int average;
@Persistent
private int bad;

@NotPersistent
private String rating;
@NotPersistent
private List ratingChoices;


@NotPersistent
private boolean alreadyPersisted;

@NotPersistent
private static final Logger logger=Logger.getLogger(SiteStatBean.class.getName());

@NotPersistent
PersistenceManagerFactory pmf=null;
/**
 * @return the key
 */
public Key getKey() {
    return key;
}
/**
 * @param key the key to set
 */
public void setKey(Key key) {
    this.key = key;
}
/**
 * @return the good
 */
public int getGood() {
   
    return good;
}
/**
 * @param good the good to set
 */
public void setGood(int good) {
    this.good = good;
}
/**
 * @return the average
 */
public int getAverage() {
    return average;
}
/**
 * @param average the average to set
 */
public void setAverage(int average) {
    this.average = average;
}
/**
 * @return the bad
 */
public int getBad() {
   
    return bad;
}
/**
 * @param bad the bad to set
 */
public void setBad(int bad) {
    this.bad = bad;
}
/**
 * @return the rating
 */
public String getRating() {
   
    return rating;
}
/**
 * @param rating the rating to set
 */
public void setRating(String rating) {
    String selected=rating;
    if(selected.equals("Good"))
        good++;
    if (selected.equals("Average"))
        average++;
    if (selected.equals("Bad"))
        bad++;
   
    PersistenceManager pm=null;
    try {
        if(pmf != null)
        {
        pm=pmf.getPersistenceManager();
       
        if(!alreadyPersisted)
        pm.makePersistent(this);
       
        else
        {
            List stats = null;
            stats=(List) pm.newQuery("select from " + SiteStatBean.class.getName()).execute();
            if(stats!=null && !stats.isEmpty())
            {
               
                for(SiteStatBean st:stats)
                {
                        st.setGood(good);
                        st.setAverage(average);
                        st.setBad(bad);
                    }
            }   
        }
       
        }
    } catch (Exception e) {
        // TODO: handle exception
        logger.severe("exception "+e.getMessage());
    }
    finally
    {
        if(pm != null && !pm.isClosed())
            {
            pm.close();
            pm=null;
            }
    }
    this.rating = rating;
}
/**
 * @return the ratingChoices
 */
public List getRatingChoices() {
   
    ratingChoices=new ArrayList();
    ratingChoices.add(new SelectItem("Good") );
    ratingChoices.add(new SelectItem("Average") );
    ratingChoices.add(new SelectItem("Bad") );
   
    return ratingChoices;
}
/**
 * @param ratingChoices the ratingChoices to set
 */
public void setRatingChoices(List ratingChoices) {
    this.ratingChoices = ratingChoices;
}

@PostConstruct
public  void init() {
// initialization code
       
    PersistenceManager pm=null;
    try {
        pmf = PMF.get();
        pm=pmf.getPersistenceManager();
       
        List stats = null;
        stats=(List) pm.newQuery("select from " + SiteStatBean.class.getName()).execute();
        if(stats!=null && !stats.isEmpty())
        {
            alreadyPersisted=true;
            for(SiteStatBean st:stats)
            {
                good+=st.getGood();
                average+=st.getAverage();
                bad+=st.getBad();
            }
           
        }
    } catch (Exception e) {
        // TODO: handle exception
        logger.severe("Exception "+e.getMessage());
    }
    finally
    {
        if(pm != null && !pm.isClosed())
            {
            pm.close();
            pm=null;
            }
    }
    //The chart will refuse to be drawn if all are 0
  //so put 1 in all
    if(good==0 && average==0 && bad==0)
     {good=1;
     average=1;
     bad=1;
     }
}
}


Read more...

  © Blogger template Simple n' Sweet by Ourblogtemplates.com 2009

Back to TOP