Gráficos JMaki-Dojo, Servlets y Java Beans

viernes, 22 de mayo de 2009
Hace unos días estuve "jugando" con Java y JMaki para crear gráficos (charts). Buscando en la red encontré el proyecto jmaki-charting, el cual permite generar gráficos Dojo de tipo Area, Linea, Barra y Torta.

Desgraciadamente, los ejemplos de gráficos Dojo están malos, si entran en la página de ejemplos y pinchan un enlace de gráficos Dojo verán lo siguiente:


Asi es que me propuse crear un ejemplo que si funcione y de eso se trata esta entrada.

La gracia del ejemplo que explicaré a continuación es que se hace uso de clases java para la creación de los gráficos. De ésta manera se pueden crear gráficos a partir de datos obtenidos dinámicamente de una base de datos o cualquier fuente que se quiera. Si bien el ejemplo usa datos "duros" fácilmente se podrían modificar las clases y traer los datos de una base de datos.

También se muestra en el ejemplo la carga de los gráficos sin la necesidad de cargar una página jsp nueva, simplemente se refresca el área correspondiente y lo demás sigue igual lo que impacta positivamente en la velocidad de carga y experiencia del usuario.

Manos a la obra. Lo primero que necesitamos es NetBeans 6.5.1 con el JDK 1.6 update 13 (con ésta versión realicé el ejemplo). Una vez instalado hay que descargar los módulos de JMaki, para eso ir a Tools -> Plugins y seleccionar los dos plugines de JMaki.

Una vez reiniciado NetBeans hay que descargar el módulo jmaki-charting, abrir NetBeans e ir a Tools -> Palette -> Add jmaki library y seleccionar el módulo previamente descargado. Una vez listo ya se pueden utilizar los gráficos Dojo en NetBeans a través de la paleta.

La aplicación es web y explicaré la lógica que hay detrás de todo. Lo primero es entender como funciona la aplicación.

Básicamente se tiene un menú donde puedes elegir el tipo de gráfico. Éste menú es un combobox Dojo, el cual se arrastra desde la paleta al index.jsp. Cuando se arrastra un combobox dojo se genera con valores por default, pero eso no es muy útil, lo mejor es cargar los datos desde un Java Bean. El java Bean utilizado es el siguiente:

package org.beans;

import org.modelo.Parametros;

/**
*
* @author metalklesk
*/
public class BeanListaCharts {

public String getValues() {
StringBuffer buffer = new StringBuffer();

buffer.append("[");
buffer.append("{label:'Area', value:'" + Parametros.AREA + "'},");
buffer.append("{label:'Barra', value:'" + Parametros.BAR + "'},");
buffer.append("{label:'Linea', value:'" + Parametros.LINE + "'},");
buffer.append("{label:'Torta', value:'" + Parametros.PIE + "'}");
buffer.append("]");

return buffer.toString();
}
}

Luego hay que ingresar la siguiente línea en el jsp para hacer uso del Bean:

<jsp:useBean id="BeanListaCharts" class="org.beans.BeanListaCharts"/>

Y luego hay que pasar del siguiente código (que viene por default al arrastrar un combobox Dojo)

<a:widget name="dojo.combobox"
value="[
{label : 'Alabama', value : 'AL'},
{label : 'California', value : 'CA'},
{label : 'New York', value : 'NY', selected : true},
{label : 'Texas', value : 'TX'}
]" />

a la siguiente forma:

<a:widget name="dojo.combobox" value="${BeanListaCharts.values}" publish="/miCombo" />

Si se fijan, el value lo rellenamos con el método getValues del Java Bean. Eventualmente esos datos podrían provenir de una base de datos.

El publish es para publicar un evento asociado al combobox. Lo que sigue es programar el evento a nuestro gusto. Esto se hace en el archivo glue.js utilizando un subscribe, en ese archivo agregamos lo siguiente (al final):

jmaki.subscribe("/miCombo/onSelect", function(args) {
/*
*Se debe cargar el grafico desde una pagina (o servlet) externa pero dentro del dominio
*e insertarla mediante el injector.
**/
var url_ = jmaki.webRoot + '/ServletGeneradorCharts?tipoChart=' + args.value;
var injector = new jmaki.Injector();
injector.inject({url:url_, injectionPoint:"chart"});
});


Aquí utilizamos un Injector para agregar código html dinámico en una parte específica de la página, sin tener que recargar todo de nuevo en una página nueva. El Injector recibe como parámetros la dirección de la página a cargar y el lugar de la página actual en la cual será insertada.

En este caso llamamos a un servlet al cual le pasamos como parámetro el valor recogido de la selección en el combobox.

El método processRequest del servlet luce de la siguiente manera:

protected void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html;charset=UTF-8");
PrintWriter out = response.getWriter();

String contexto = request.getContextPath();

String seleccion = request.getParameter("tipoChart");
String chart = "No existe ese tipo de gráfico.";

if(seleccion.equalsIgnoreCase(Parametros.PIE)) {
PieChartDojoJMaki pie = new PieChartDojoJMaki();
//si no le pasamos los parámetros requeridos, se genera un pie de ejemplo
//con valores predeterminados.
chart = pie.getValor();

out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/base.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/excanvas.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/PlotKit_Packed.js"></script>");
out.println("<link rel="stylesheet" type="text/css" href="" + contexto + "/resources/jmaki/charting/resources/jmaki-charting.css" />");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/pie/component.js"></script>");
out.println("<script type="text/javascript">jmaki.addWidget({widgetDir:'" + contexto + "/resources/jmaki/charting/pie',name:'jmaki.charting.pie',value:" + chart + ",uuid:'jmaki_charting_pie'});</script>");
out.println("<div id="jmaki_charting_pie" class="jmCPie">");
out.println("<canvas id="jmaki_charting_pie_chart" class="jmCPie"></canvas>");
out.println("</div>");
out.close();
} else if(seleccion.equalsIgnoreCase(Parametros.AREA)) {
AreaChartDojoJMaki area = new AreaChartDojoJMaki();
//si no le pasamos los parámetros requeridos, se genera un pie de ejemplo
//con valores predeterminados.
chart = area.getValor();

out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/base.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/excanvas.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/PlotKit_Packed.js"></script>");
out.println("<link rel="stylesheet" type="text/css" href="" + contexto + "/resources/jmaki/charting/resources/jmaki-charting.css" />");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/area/component.js"></script>");
out.println("<script type="text/javascript">jmaki.addWidget({widgetDir:'" + contexto + "/resources/jmaki/charting/area',name:'jmaki.charting.area',value:" + chart + ",uuid:'jmaki_charting_area2'});</script>");
out.println("<div id="jmaki_charting_area2" class="jmCPie">");
out.println("<canvas id="jmaki_charting_area2_chart" class="jmCPie"></canvas>");
out.println("</div>");
out.close();
} else if(seleccion.equalsIgnoreCase(Parametros.BAR)) {
BarChartDojoJMaki bar = new BarChartDojoJMaki();
//si no le pasamos los parámetros requeridos, se genera un pie de ejemplo
//con valores predeterminados.
chart = bar.getValor();

out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/base.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/excanvas.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/PlotKit_Packed.js"></script>");
out.println("<link rel="stylesheet" type="text/css" href="" + contexto + "/resources/jmaki/charting/resources/jmaki-charting.css" />");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/bar/component.js"></script>");
out.println("<script type="text/javascript">jmaki.addWidget({widgetDir:'" + contexto + "/resources/jmaki/charting/bar',name:'jmaki.charting.bar',value:" + chart + ",uuid:'jmaki_charting_bar'});</script>");
out.println("<div id="jmaki_charting_bar" class="jmCPie">");
out.println("<canvas id="jmaki_charting_bar_chart" class="jmCPie"></canvas>");
out.println("</div>");
out.close();
} else if(seleccion.equalsIgnoreCase(Parametros.LINE)) {
LineChartDojoJMaki line = new LineChartDojoJMaki();
//si no le pasamos los parámetros requeridos, se genera un pie de ejemplo
//con valores predeterminados.
chart = line.getValor();

out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/base.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/excanvas.js"></script>");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/resources/plotkit/PlotKit_Packed.js"></script>");
out.println("<link rel="stylesheet" type="text/css" href="" + contexto + "/resources/jmaki/charting/resources/jmaki-charting.css" />");
out.println("<script type="text/javascript" src="" + contexto + "/resources/jmaki/charting/line/component.js"></script>");
out.println("<script type="text/javascript">jmaki.addWidget({widgetDir:'" + contexto + "/resources/jmaki/charting/line',name:'jmaki.charting.line',value:" + chart + ",uuid:'jmaki_charting_line'});</script>");
out.println("<div id="jmaki_charting_line" class="jmCPie">");
out.println("<canvas id="jmaki_charting_line_chart" class="jmCPie"></canvas>");
out.println("</div>");
out.close();
} else {
//si el tipo de gráfico no corresponde, notifico al usuario.
out.println("<font color="#f00">" + chart + "</font>");
out.close();
}
}

El servlet recibe el parámetro seleccionado por el usuario a través del request.getParameter y dependiendo del valor construye un gráfico. Para obtener los valores del widget se hace uso de las clases

  • AreaChartDojoJMaki
  • BarChartDojoJMaki
  • LineChartDojoJMaki
  • PieChartDojoJMaki
Cada una de éstas clases se especializa en construir un gráfico en particular, utilizando la notación definida por JMaki (Dojo en este caso). En el ejemplo se utilizan datos duros, pero al igual que el caso del combobox, fácilmente se pueden adaptar para hacer uso de una base de datos.

A continuación se puede ver el código de la clase que define un gráfico de area:

package org.modelo;

import java.util.ArrayList;

/**
*
* @author metalklesk
*/
public class AreaChartDojoJMaki {

private ArrayList<String> xEtiquetas;
private ArrayList<String> yEtiquetas;
private ArrayList<String> datos;

/**
* Constructor sin parámetros. Inicia valores predeterminados para construir un
* gráfico de ejemplo.
*/
public AreaChartDojoJMaki() {
xEtiquetas = new ArrayList<String>();
xEtiquetas.add("{label : 'Enero'}, ");
xEtiquetas.add("{label : 'Febrero'}, ");
xEtiquetas.add("{label : 'Marzo'}, ");
xEtiquetas.add("{label : 'Abril'}, ");
xEtiquetas.add("{label : 'Mayo'}, ");
xEtiquetas.add("{label : 'Junio'}, ");
xEtiquetas.add("{label : 'Julio'}, ");
xEtiquetas.add("{label : 'Agosto'}, ");
xEtiquetas.add("{label : 'Septiembre'}, ");
xEtiquetas.add("{label : 'Octubre'}, ");
xEtiquetas.add("{label : 'Noviembre'}, ");
xEtiquetas.add("{label : 'Diciembre'}");

yEtiquetas = new ArrayList<String>();
yEtiquetas.add("{ label : '0', value : 0}");
yEtiquetas.add("{ label : '10s', value : 10}");
yEtiquetas.add("{ label : '20s', value : 20}");
yEtiquetas.add("{ label : '30s', value : 30}");
yEtiquetas.add("{ label : '40s', value : 40}");
yEtiquetas.add("{ label : '50s', value : 50}");

datos = new ArrayList<String>();
datos.add("{id : 'marcador', label : 'Dataset 1', values : [25, 45, 25, 45, 50, 25, 35, 25, 25, 20, 35, 45] }");
datos.add("{label : 'Dataset 2', values : [20, 40, 30, 35, 45, 20, 25, 15, 20, 25, 30, 40] }");
datos.add("{label : 'Dataset 3', values : [15, 35, 15, 40, 30, 15, 20, 10, 15, 20, 30, 35] }");
datos.add("{label : 'Dataset 4', values : [10, 25, 10, 5, 20, 5, 15, 5, 10, 15, 25, 30] }");
}

/**
* Recibe los datos que construyen el gráfico. Se pueden ingresar varios conjuntos de datos (datasets) y la forma
* en que se ingresa cada uno es de la siguiente manera:
* "{label : '$titulo_dataset', values : [$valor1, $valor2, ..., $valorN]}", sin las comillas.
* @param datos
*/
public void setDatos(ArrayList<String> datos) {
this.datos = datos;
}

/**
* Recibe las etiquetas para la coordenada X. El formato de cada etiqueta debe ser de la siguiente forma:
* "{label: '$valor'},", sin las comillas.
* @param xEtiquetas
*/
public void setXEtiquetas(ArrayList<String> xEtiquetas) {
this.xEtiquetas = xEtiquetas;
}

/**
* Recine las etiquetas para la coordenada Y. El formato de cada etiqueta debe ser de la siguiente forma:
* "{label : '$titulo', value : $valor}"
* @param yEtiquetas
*/
public void setYEtiquetas(ArrayList<String> yEtiquetas) {
this.yEtiquetas = yEtiquetas;
}

/**
* Retorna el contenido de la etiqueta value del componente jmaki.charting.area.
* @return
*/
public String getValor() {
StringBuffer buffer = new StringBuffer();

buffer.append("{");
//valores de la coordenada x
buffer.append("xAxis : { labels : [ ");
for(int i=0; i< xEtiquetas.size(); i++) {
buffer.append(xEtiquetas.get(i));
}
buffer.append("]},");
//valores de la coordenada y
buffer.append("yAxis : { labels : [");
for(int i=0; i< yEtiquetas.size(); i++) {
buffer.append(yEtiquetas.get(i) + ", ");
}
buffer.append("]},");
//aqui agrego marcadores en algunos puntos.
buffer.append("markers : [{ targetId : 'marcador', label : '{value}', " +
"index : 5}, { targetId : 'marcador', label : '{value}', index : 6}, " +
"{ targetId : 'marcador', label : '{value}', index : 8}],");
//datos que construyen el grafico
buffer.append("data : [ ");
for(int i=0; i<datos.size(); i++) {
buffer.append(datos.get(i) + ", ");
}
buffer.append("]");

buffer.append("}");

return buffer.toString();
}
}

Como se pude ver, no es necesario utilizar objetos JSON, un simple String con la estructura adecuada basta para generar un chart o widget JMaki en general.

A continuación se pueden ver imágenes del ejemplo funcionando.



Como siempre pueden dejar un comentario con su dirección de correo y les envío el ejemplo completo (proyecto en NetBeans 6.5.1 comprimido) para que lo corran y vean en sus equipos.

Saludos !!!

    19 comentarios:

    Carol Johana Rodriguez dijo...

    oye esta chever tu post, de casualidad tienes algo que saque graficos apartir de losd atos de una base de datos, ene ste momento utilizo netbeans 5.5 y postgresql muchas gracias si me puedes ayudar o enviarme tu poryecto mi correo es caroljohanar@gmail.com

    Daniel Dario Morales Salas dijo...

    Hola

    No tengo un ejemplo de eso pero no es para nada dificil. Simplemente te conectas por JDBC a la base de datos y obtienes los datos que quieres graficar. Por lo general los datos deberias tenerlos en un arreglo o lista dinámica. Una vez tengas los datos simplemente agregalos al gráfico que quieras.

    Puedes usar el ejemplo de esta misma entrada, solo debes agregarle la conexion a la base de datos y la obtencion de los datos, luego puedes usar las clases que hice para cada gráfico e insertarle los datos que obtuviste de la base de datos. Es así de simple.

    Saludos!

    Alberto de Viña dijo...

    Hola Viejo... Me parece un buen ejemplo, en especial estuve trabajando con uno que realizaste hace mucho, que tiene que ver como crear un pool de conexiones y llamarlo desde una clase java.
    La verdad que tambien aprovecho de solicitarte, si sabes de algo relacionado con struts, o tienes un ejemplo como para poder tener una base, ya que siempre hay cosas muy sencillas ..
    Bueno, ante todo, muchas gracias por tus ejemplos publicados

    Daniel Dario Morales Salas dijo...

    La verdad con struts nunca he trabajado asi que por ahora no te puedo ayudar en eso :(

    Saludos!!

    Anónimo dijo...

    Hola :D oye esta exelente tu blog, este articulo me ayudaria mucho a poner graficas para un proyecto de la escuela, si pudieras mandarmelo seria genial (mi mail es : hm_cuesta@yahoo.com.mx ) porfa te lo agradecere mucho

    andryi dijo...

    OLa Daniel, revisando tu ejemplo, para lo q son bd, como dijo carol, podrias tbn enviarme tu ejemplo para revisarlo. anexo mi correo andryi777@gmail.com, de antemano gracias por el aporte

    Anónimo dijo...

    Interesante tu ejemplo, me puedes mandar los ejemplos que tengas lo mas pronto posible, es que estoy contra el tiempo y necesito entregar una tarea con graficos, de antemano se te agradece.
    Mi email jairomald@gmail.com

    JuliandresOG dijo...
    Este comentario ha sido eliminado por el autor.
    Nilton Zambrano dijo...

    Hola

    El ejemplo de graficos esta muy interesante, puedes mandarme el ejemplo a nilton.zambrano@gmail.com

    Muchas gracias

    Anónimo dijo...

    Hola super bueno tu post, no conocia que se podria hacer eso con jmaki-Dojo, podrias enviarme el ejemplo a mi correo khanservero@hotmail.com

    Muchas gracias de antemano.

    Javito dijo...

    Hola,

    Buen trabajo!!! me interesa mucho ver como funciona en detalle, ¿podrías enviarmelo todavía? ( javier_linan@hotmail.com ) Muchas gracias por todo.

    Anónimo dijo...

    olas,
    esta genial segun lo que veo es casi justo lo que necesito para hacer mi trabajo.....pero tengo una duda!! cuando dices datos duros te refieres a archivos txt ???
    me lo puedes enviar a mi correo porfa..
    l.giasthon@gmail.com
    t lo agradeceria mxo.

    Daniel Dario Morales Salas dijo...

    con datos duros me refiero a que están en el mismo código, no los estoy tomando de un txt o de una base de datos ni ninguna fuente de datos dinámica.

    Saludos!!!

    Unknown dijo...

    Saludos.
    Magnífico aporte, me interesa mucho ver como funciona en detalle este ejemplo, ¿sería mucha molestría que me enviarás una copia del proyecto de NetBeans a mi correo?

    rulo.rojas@gmail.com

    Te lo agradecería mucho.

    Frank dijo...

    Hola interesante tu post me gustaria que me enviaras el proyecto
    a frankrl86@gmail.com

    Gustavo E Fuentes M dijo...

    Muy bueno tu post. Me funcionó muy bien. Tus explicaciones fueron precisas y completas.
    Te felicito.

    pedro dijo...

    aprovechando la situación me gustarla que me mandaras tu aplicación pues me servirá de mucho,además espero y me puedas ayudar pues necesito colocar un reproductor en un Area bean desde Oracle Developer 10g, de antemano muchas gracias.

    pedro985_@hotmail.com

    Franklyn dijo...

    Hola, es muy interesante tu ejemplo. Me gustaria poder contar con el proyecto para fines de aprendizaje.
    Muchas gracias!

    FranklynVidal@gmail.com

    Anónimo dijo...

    puesssss la verdad no me parece esta pagina no sirve para nada

    Publicar un comentario